99e93ae948319a18c75e861be05737f96033e139
[iot2.git] / benchmarks / other / XbeePythonDriver / xbee_driver.py
1 from xbee import ZigBee
2 import serial
3 import time
4 import collections
5 import sys
6 import getopt
7 from socket import *
8 import traceback
9 from threading import Thread, Lock
10 import random
11 import threading
12
13
14 # -----------------------------------------------------------------------------
15 # Constants ans Pseudo-Constants
16 # -----------------------------------------------------------------------------
17 UDP_RECEIVE_PORT = 5005        # port used for incoming UDP data
18 UDP_RECEIVE_BUFFER_SIZE = 4096  # max buffer size of an incoming UDP packet
19 SYSTEM_MASTER_ADDRESS = ("192.168.2.108", 12345) # ip address and portof the system master node
20
21 # time for messages to wait for a response before the system clears away that 
22 # sequence identifier
23 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5 
24
25 #ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH"  # USB-Serial port of local radio
26 ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
27 ZIGBEE_SERIAL_BAUD = 115200                       # Baud rate for above port
28
29 # address of our local zigbee radio
30 #ZIGBEE_DEVICE_ADDRESS = "0013a20040d99cb4"
31 ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx"
32
33 # -----------------------------------------------------------------------------
34 # Global Variables and Objects
35 # -----------------------------------------------------------------------------
36
37 # signals to see if a request needs to be made
38 didGetLocalRadioHighAddress = False;
39 didGetLocalRadioLowAddress = False;
40
41 # zigbee communications object and its mutex
42 zigbeeConnection = None
43 zigbeeConnectionMutex = Lock()
44
45 #singleton mabe by changwoo
46 matchDescriptorReqSingleton = True
47 deviceAnnouncementSingleton = True
48 ManagementPermitJoiningReqSuccess = False
49
50 # zigbee mapping from long to short object dict
51 zigbeeLongShortAddr = dict()
52 zigbeeLongShortAddrMutex = Lock()
53
54 # zigbee mapping from a sequence number to a client 
55 # for correct response handling
56 zigbeeSeqNumberToClient = dict()
57 zigbeeSeqNumberToClientMutex = Lock()
58
59 zigeeBindRequest = dict()
60 zigeeBindRequestMutex = Lock()
61
62 # Keeps record of where to send callbacks to when an HA message is received
63 zibeeHACallback = dict()
64 zibeeHACallbackMutex = Lock()
65
66
67 # Keeps a record of device addresses whose short addresses have not been 
68 # determined yet
69 zigbeeUnregisteredAddresses = []
70 zigbeeUnregisteredAddressesMutex = Lock()
71
72 # used to signal all threads to end
73 doEndFlag = False
74
75
76 # 2 sockets, one for sending (not bound to a port manually)
77 # and one for receiving, known port binding by application
78 # both UDP sockets
79 sendSoceket = socket(AF_INET, SOCK_DGRAM)
80 receiveSoceket = socket(AF_INET, SOCK_DGRAM)
81
82 # zigbee address authority list
83 zigbeeAddressAuthorityDict = dict()
84
85 # made by changwoo
86 seqNumberForNotification = dict()
87
88 # -----------------------------------------------------------------------------
89 # Helper Methods
90 # -----------------------------------------------------------------------------
91 def reverseShortAddress(shortAddr):
92     result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
93     return result
94
95 def parseCommandLineArgs(argv):
96     global ZIGBEE_SERIAL_PORT
97     global ZIGBEE_SERIAL_BAUD
98     try:
99         opts, args = getopt.getopt(
100             argv, "hp:b:u:", ["port=", "baud=", "udpport="])
101
102     except getopt.GetoptError:
103         print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
104         sys.exit(2)
105
106     for opt, arg in opts:
107         if opt == '-h':
108             print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
109             sys.exit()
110
111         elif opt in ("-p", "--port"):
112             ZIGBEE_SERIAL_PORT = arg
113
114         elif opt in ("-b", "--baud"):
115             try:
116                 ZIGBEE_SERIAL_BAUD = int(arg)
117             except ValueError:
118                 print "Buad rate must be an integer"
119                 sys.exit()
120
121
122 # -------------
123 # Convenience (Stateless)
124 # -------------
125
126 def hexListToChar(hexList):
127     ''' Method to convert a list/string of characters into their corresponding values
128
129         hexList -- list or string of hex characters
130     '''
131     retString = ""
132     for h in hexList:
133         retString += chr(int(h, 16))
134     return retString
135
136 def splitByN(seq, n):
137     ''' Method to split a string into groups of n characters
138
139         seq -- string
140         n -- group by number
141     '''
142     return [seq[i:i+n] for i in range(0, len(seq), n)]
143
144 def changeEndian(hexString):
145     ''' Method to change endian of a hex string
146
147         hexList -- string of hex characters
148     '''
149     split = splitByN(hexString, 2) # get each byte
150     split.reverse();               # reverse ordering of the bytes
151
152     # reconstruct 
153     retString = ''
154     for s in split:
155         retString += s
156     return retString
157
158 def printMessageData(data):
159     ''' Method to print a zigbee message to the console
160
161         data -- pre-parsed zigbee message
162     '''
163     for d in data:
164         print d, ' : ',
165         for e in data[d]:
166             print "{0:02x}".format(ord(e)),
167         if (d == 'id'):
168             print "({})".format(data[d]),
169         print
170
171 def hexStringToZigbeeHexString(hexString):
172     ''' Method to change a hex string to a string of characters with the hex values
173
174         hexList -- string of hex characters
175     '''
176     return hexListToChar(splitByN(hexString, 2))
177
178 def zigbeeHexStringToHexString(zigbeeHexString):
179     ''' Method to change string of characters with the hex values to a hex string
180
181         hexList -- string of characters with hex values
182     '''
183
184     retString = ''
185     for e in zigbeeHexString:
186         retString += "{0:02x}".format(ord(e))
187     return retString
188
189 def zclDataTypeToBytes(zclPayload):
190     ''' Method to determine data length of a zcl attribute
191
192         zclPayload -- ZCL payload data, must have dataType as first byte
193     '''
194     attrType = ord(zclPayload[0])
195
196     if(attrType == 0x00):
197         return 0
198     elif (attrType == 0x08):
199         return 1
200     elif (attrType == 0x09):
201         return 2
202     elif (attrType == 0x0a):
203         return 3
204     elif (attrType == 0x0b):
205         return 4
206     elif (attrType == 0x0c):
207         return 5
208     elif (attrType == 0x0d):
209         return 6
210     elif (attrType == 0x0e):
211         return 7
212     elif (attrType == 0x0f):
213         return 8
214     elif (attrType == 0x10):
215         return 1
216     elif (attrType == 0x18):
217         return 1
218     elif (attrType == 0x19):
219         return 2
220     elif (attrType == 0x1a):
221         return 3
222     elif (attrType == 0x1b):
223         return 4
224     elif (attrType == 0x1c):
225         return 5
226     elif (attrType == 0x1d):
227         return 6
228     elif (attrType == 0x1e):
229         return 7
230     elif (attrType == 0x1f):
231         return 8
232     elif (attrType == 0x20):
233         return 1
234     elif (attrType == 0x21):
235         return 2
236     elif (attrType == 0x22):
237         return 3
238     elif (attrType == 0x23):
239         return 4
240     elif (attrType == 0x24):
241         return 5
242     elif (attrType == 0x25):
243         return 6
244     elif (attrType == 0x26):
245         return 7
246     elif (attrType == 0x27):
247         return 8
248     elif (attrType == 0x28):
249         return 1
250     elif (attrType == 0x29):
251         return 2
252     elif (attrType == 0x2a):
253         return 3
254     elif (attrType == 0x2b):
255         return 4
256     elif (attrType == 0x2c):
257         return 5
258     elif (attrType == 0x2d):
259         return 6
260     elif (attrType == 0x2e):
261         return 7
262     elif (attrType == 0x2f):
263         return 8
264     elif (attrType == 0x30):
265         return 1
266     elif (attrType == 0x31):
267         return 2
268     elif (attrType == 0x38):
269         return 2
270     elif (attrType == 0x39):
271         return 4
272     elif (attrType == 0x3a):
273         return 8
274     elif (attrType == 0x41):
275         return ord(zclPayload[1])
276     elif (attrType == 0x42):
277         return ord(zclPayload[1])
278     elif (attrType == 0x43):
279         return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
280     elif (attrType == 0x44):
281         return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
282     elif (attrType == 0xe0):
283         return 4
284     elif (attrType == 0xe1):
285         return 4
286     elif (attrType == 0xe2):
287         return 4
288     elif (attrType == 0xe8):
289         return 2
290     elif (attrType == 0xe9):
291         return 2
292     elif (attrType == 0xea):
293         return 4
294     elif (attrType == 0xf0):
295         return 8
296     elif (attrType == 0xf1):
297         return 16
298     elif (attrType == 0xff):
299         return 0
300
301 # -------------
302 # Other
303 # -------------
304
305 def createSequenceNumberForClient(addr, packetId):
306     ''' Method to get and store a sequence number with a specific client 
307         for a specific message.
308
309         addr -- UDP address of the client to associate with the seq. number
310         packetId -- packet id from the UDP packet
311     '''
312     # keep trying to find a number to return
313     while(True):
314
315         # get the current time
316         epoch_time = int(time.time())
317
318         # get the current list of used numbers
319         zigbeeSeqNumberToClientMutex.acquire()
320         keysList = zigbeeSeqNumberToClient.keys()
321         zigbeeSeqNumberToClientMutex.release()
322     
323         # if all the numbers are already used
324         if(len(keysList) == 256):
325
326             # get a list of all the items
327             zigbeeSeqNumberToClientMutex.acquire()
328             itemsList = zigbeeSeqNumberToClient.items()
329             zigbeeSeqNumberToClientMutex.release()
330
331             # search for a number that is old enough to get rid of otherwise use -1
332             seqNum = -1
333             for item in itemsList:
334                 if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC):
335                     seqNumber = item[0]
336                     break
337
338             if(seqNum != -1):
339                 # replace the record with new data if we found one to replace
340                 zigbeeSeqNumberToClientMutex.acquire()
341                 zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId)
342                 zigbeeSeqNumberToClientMutex.release()
343
344             return seqNumber
345             
346         else:
347             # not all numbers used yet so pick one randomly
348             randNum = random.randrange(0,256)
349
350             # check if we are using the number yet
351             if(randNum not in keysList):
352
353                 # we are not so insert to keep track who this number is for and return it
354                 zigbeeSeqNumberToClientMutex.acquire()
355                 zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId)
356                 zigbeeSeqNumberToClientMutex.release()
357                 return randNum
358
359 def getConnectedRadioLongAddress():
360     """ Method to make sure we get the MAC address of our local radio"""
361     global zigbeeConnection
362     global zigbeeMutex
363
364     # keep looping until we get both the MSBs and the LSBs
365     while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)):
366
367         # reissue requests
368         zigbeeConnection.send('at', command="SH")
369         zigbeeConnection.send('at', command="SL")
370         
371         # sleep for a bit to give the radio time to respond before we check again
372         time.sleep(2)
373
374 def addressUpdateWorkerMethod():
375     ''' Method to keep refreshing the short addresses of the known zigbee devices'''
376     global doEndFlag
377     global zigbeeLongShortAddr
378     global zigbeeLongShortAddrMutex
379     global zigbeeUnregisteredAddresses
380     global zigbeeUnregisteredAddressesMutex
381     global zigbeeConnectionMutex
382     global zigbeeConnection
383
384     # keep looping until signaled to quit
385     while(not doEndFlag):
386
387         addrList = []
388
389         # add unregistered (short addresses unknown) devices so
390         # that we can look them up
391         zigbeeUnregisteredAddressesMutex.acquire()
392         addrList.extend(zigbeeUnregisteredAddresses)
393         zigbeeUnregisteredAddressesMutex.release()
394
395         # add the devices that we have short addresses for so we can 
396         # get their most recent short addresses
397         zigbeeLongShortAddrMutex.acquire()
398         addrList.extend(zigbeeLongShortAddr.keys())
399         zigbeeLongShortAddrMutex.release()
400
401         # Loop through all the addresses and send messages for each address
402         for ad in addrList:
403
404             # create payload for a query on the network for a short address
405             payload = '\x00'
406             payload += hexStringToZigbeeHexString(changeEndian(ad))
407             payload += '\x00'
408
409             # create and send binding command
410             zigbeeConnectionMutex.acquire()
411             
412             zigbeeConnection.send('tx_explicit',
413                                 frame_id='\x01',
414                                 dest_addr_long=hexStringToZigbeeHexString(ad),
415                                 dest_addr='\xff\xfd',
416                                 src_endpoint='\x00',
417                                 dest_endpoint='\x00',
418                                 cluster='\x00\x00',  
419                                 profile='\x00\x00',
420                                 data=payload
421                                 )
422             zigbeeConnectionMutex.release()
423
424         time.sleep(8)
425
426
427 # -------------
428 # UDP 
429 # -------------
430
431 def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
432     ''' Method to send a success or fail back to a client.
433
434         addr -- UDP address to send packet to
435         packetTypeStr -- name of this specific packet
436         packetIdStr -- packet id to send
437         sucOrFail -- whether this is a success or fail message (True = success)
438         reason -- reason of failure (if needed, default is None)
439
440     '''
441
442     global sendSoceket
443
444     # construct the message
445     message = "type: " + packetTypeStr.strip() + "\n"
446     message += "packet_id: " + packetIdStr + "\n"
447
448     if(sucOrFail):
449         message += "response: success \n"
450     else:
451         message += "response: fail \n"
452         message += "reason: " + reason + "\n"
453
454     # send message in a UDP packet
455     sendSoceket.sendto(message,addr)
456
457 def processUdpZdoBindReqMessage(parsedData, addr):
458
459     shortAddr = None
460
461     if(zigbeeAddressAuthorityDict.has_key(addr)):
462         l = zigbeeAddressAuthorityDict[addr]
463         if(parsedData['device_address_long'] not in l):
464             return
465     else:
466         return
467
468     # get the short address for this device long address if possible
469     zigbeeLongShortAddrMutex.acquire()
470     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
471         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
472     zigbeeLongShortAddrMutex.release()
473
474     # if there is a short address than we can send the message
475     # if there is not one then we cannot since we need both the short and
476     # the long address
477     if(shortAddr != None):
478
479         # get a request number
480         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
481         
482         # send back failure
483         if(seqNumber == -1):
484
485             # send an error message, could not get a sequence number to use at this time
486             sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
487             return
488
489         # a bind request was made so must store and wait for response 
490         # before we setup callbacks, so keep just the data we need to create the callback
491         zigeeBindRequestMutex.acquire()
492         zigeeBindRequest[seqNumber] = (parsedData['device_address_long'],
493                                         parsedData['cluster_id'], 
494                                         parsedData['packet_id'], 
495                                         addr)
496         zigeeBindRequestMutex.release()
497
498         # construct the short and long addresses of the message for sending
499         # make sure they are in the correct format
500         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
501         destShortAddr = hexStringToZigbeeHexString(shortAddr)
502
503         # create the payload data
504         payloadData = ""
505         payloadData += chr(seqNumber)
506         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
507         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
508         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) 
509         payloadData += '\x03' 
510         payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
511         payloadData += '\x00'
512
513         # create and send binding command
514         zigbeeConnectionMutex.acquire()
515         zigbeeConnection.send('tx_explicit',
516                             frame_id='\x01',
517                             # frame_id=chr(seqNumber),
518                             dest_addr_long=destLongAddr,
519                             dest_addr=destShortAddr,
520                             src_endpoint='\x00',
521                             dest_endpoint='\x00',
522                             cluster='\x00\x21',  
523                             profile='\x00\x00',
524                             data=payloadData
525                             )
526         zigbeeConnectionMutex.release()
527
528
529     else:
530         # send a failure packet since there is no short address available
531         sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
532         pass
533
534 def processUdpZdoUnBindReqMessage(parsedData, addr):
535     zibeeHACallbackMutex.acquire();
536     if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
537         zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
538     zibeeHACallbackMutex.release()
539     sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
540
541
542
543 def processUdpSendAddressMessage(parsedData, addr):
544     ''' Method handle a send address command
545
546         parsedData -- Pre-parsed Data that was in the UDP packet.
547         addr -- Address (IP and Port) of the UDP packet origin.
548     '''
549     global zigbeeLongShortAddr
550     global zigbeeLongShortAddrMutex
551     global zigbeeUnregisteredAddresses
552     global zigbeeUnregisteredAddressesMutex
553     global sendSoceket
554
555     print "process send address"
556     
557
558     # construct success message
559     message = "type: send_address_response\n"
560     message += "packet_id: " + parsedData['packet_id'] + "\n"
561     message += "response: success\n"
562
563     # tell client that we got their request
564     sendSoceket.sendto(message,addr)
565     print "responding", message
566     
567     # construct 
568     zigbeeLongShortAddrMutex.acquire()
569     doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
570     zigbeeLongShortAddrMutex.release()
571
572     if(doesHaveKey):
573         # long address is already registered with the system so no need to do anything
574         return
575
576     # long address not registered so add it for short address lookup
577     zigbeeUnregisteredAddressesMutex.acquire()
578     zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
579     zigbeeUnregisteredAddressesMutex.release()
580
581 #made by changwoo
582 def processUdpEnrollmentResponse(parsedData, addr):
583
584     global zigbeeLongShortAddr
585     global zigbeeLongShortAddrMutex
586     global zigeeBindRequestMutex
587     global zigeeBindRequest
588     global zigbeeConnectionMutex
589     global zigbeeConnection
590     shortAddr = None
591
592     # get the short address for this device long address if possible
593     zigbeeLongShortAddrMutex.acquire()
594     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
595         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
596     zigbeeLongShortAddrMutex.release()
597
598
599     # if there is a short address than we can send the message
600     # if there is not one then we cannot since we need both the short and
601     # the long address
602     if(shortAddr != None):
603
604         # get a request number
605         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
606         
607         # send back failure
608         if(seqNumber == -1):
609
610             # send an error message, could not get a sequence number to use at this time
611             sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
612             return
613
614         # get the info for sending
615         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
616         destShortAddr = hexStringToZigbeeHexString(shortAddr)
617         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
618         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
619         profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
620
621         # create the payload data
622         payloadData = ""
623         payloadData += '\x01'
624         payloadData += chr(seqNumber)
625         payloadData += '\x00'
626         payloadData += '\x00\x00'
627
628         # create and send binding command
629         zigbeeConnectionMutex.acquire()
630         zigbeeConnection.send('tx_explicit',
631                             frame_id='\x40',
632                             # frame_id=chr(seqNumber),
633                             dest_addr_long=destLongAddr,
634                             dest_addr=destShortAddr,
635                             src_endpoint='\x01',
636                             dest_endpoint=dstEndpoint,
637                             cluster=clusterId,  
638                             profile=profileId,
639                             data=payloadData
640                             )
641         print '> EnrollmentResponse is sent'
642         zigbeeConnectionMutex.release()
643
644
645     else:
646         # send a fail response
647         sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
648         pass
649
650
651
652
653 #made by changwoo
654 def processUdpZclWriteAttributesMessage(parsedData, addr):
655
656     global zigbeeLongShortAddr
657     global zigbeeLongShortAddrMutex
658     global zigeeBindRequestMutex
659     global zigeeBindRequest
660     global zigbeeConnectionMutex
661     global zigbeeConnection
662     shortAddr = None
663
664     # get the short address for this device long address if possible
665     zigbeeLongShortAddrMutex.acquire()
666     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
667         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
668     zigbeeLongShortAddrMutex.release()
669
670     # if there is a short address than we can send the message
671     # if there is not one then we cannot since we need both the short and
672     # the long address
673     if(shortAddr != None):
674         # get a request number
675         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
676         
677         # send back failure
678         if(seqNumber == -1):
679
680             # send an error message, could not get a sequence number to use at this time
681             sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
682             return
683
684         # get the info for sending
685         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
686         destShortAddr = hexStringToZigbeeHexString(shortAddr)
687         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
688         profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
689         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
690
691         # create the payload data
692         payloadData = ""
693         payloadData += '\x00'
694         payloadData += chr(seqNumber)
695         payloadData += '\x02'
696         payloadData += '\x10\x00'
697         payloadData += '\xF0'
698 #        payloadData += '\xDA\x9A\xD9\x40\x00\xA2\x13\x00'
699         payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
700
701         zigbeeConnectionMutex.acquire()
702         zigbeeConnection.send('tx_explicit',
703                             frame_id='\x08',
704                             # frame_id=chr(seqNumber),
705                             dest_addr_long=destLongAddr,
706                             dest_addr=destShortAddr,
707                             src_endpoint='\x01',
708                             dest_endpoint=dstEndpoint,
709                             cluster=clusterId,
710                             profile=profileId,
711                             data=payloadData
712                             )
713
714         print ''
715         print '> WriteAttributesReq is sent : '+str(shortAddr)
716         zigbeeConnectionMutex.release()
717
718
719     else:
720         # send a fail response
721         sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
722         pass
723
724 #made by changwoo
725 def processUdpZclChangeSwitchReqMessage(parsedData, addr):
726
727     global zigbeeLongShortAddr
728     global zigbeeLongShortAddrMutex
729     global zigeeBindRequestMutex
730     global zigeeBindRequest
731     global zigbeeConnectionMutex
732     global zigbeeConnection
733     shortAddr = None
734
735     # get the short address for this device long address if possible
736     zigbeeLongShortAddrMutex.acquire()
737     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
738         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
739     zigbeeLongShortAddrMutex.release()
740
741
742     # if there is a short address than we can send the message
743     # if there is not one then we cannot since we need both the short and
744     # the long address
745     if(shortAddr != None):
746
747         # get a request number
748         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
749
750         # send back failure
751         if(seqNumber == -1):
752
753             # send an error message, could not get a sequence number to use at this time
754             sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
755             return
756
757         # get the info for sending
758         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
759         destShortAddr = hexStringToZigbeeHexString(shortAddr)
760         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
761         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
762         profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
763         value = hexStringToZigbeeHexString(parsedData['value'])
764
765         # create and send binding command
766         zigbeeConnectionMutex.acquire()
767
768         zigbeeConnection.send('tx_explicit',
769                             frame_id='\x40',
770                             # frame_id=chr(seqNumber),
771                             dest_addr_long=destLongAddr,
772                             dest_addr=destShortAddr,
773                             src_endpoint='\x01',
774                             dest_endpoint=dstEndpoint,
775                             cluster=clusterId,  
776                             profile=profileId,
777                             data='\x01'+chr(seqNumber)+value
778                             )
779         time.sleep(1)
780         if parsedData['value']==1:
781                 print '> The outlet sensor turned on'
782         else :
783                 print '> The outlet sensor turned off'
784
785         zigbeeConnectionMutex.release()
786
787
788     else:
789         # send a fail response
790         sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
791         pass
792
793
794
795 # made by changwoo
796 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
797
798     global zigbeeLongShortAddr
799     global zigbeeLongShortAddrMutex
800     global zigeeBindRequestMutex
801     global zigeeBindRequest
802     global zigbeeConnectionMutex
803     global zigbeeConnection
804     shortAddr = None
805
806     # get the short address for this device long address if possible
807     zigbeeLongShortAddrMutex.acquire()
808     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
809         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
810     zigbeeLongShortAddrMutex.release()
811
812
813     # if there is a short address than we can send the message
814     # if there is not one then we cannot since we need both the short and
815     # the long address
816     if(shortAddr != None):
817
818         # get a request number
819         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
820
821         # send back failure
822         if(seqNumber == -1):
823
824             # send an error message, could not get a sequence number to use at this time
825             sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
826             return
827
828         # get the info for sending
829         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
830         destShortAddr = hexStringToZigbeeHexString(shortAddr)
831         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
832
833         # create and send binding command
834         zigbeeConnectionMutex.acquire()
835
836         zigbeeConnection.send('tx_explicit',
837                             frame_id='\x01',
838                             # frame_id=chr(seqNumber),
839                             dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
840                             dest_addr='\xff\xfe',
841                             src_endpoint='\x00',
842                             dest_endpoint=dstEndpoint,
843                             cluster='\x00\x32',  
844                             profile='\x00\x00',
845                             data='\x12'+'\x01'
846                             )
847         time.sleep(1)
848         print '> BroadcastingRouteRecordReq is sent'
849
850         zigbeeConnectionMutex.release()
851
852
853     else:
854         # send a fail response
855         sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
856         pass
857
858
859 #made by changwoo
860 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
861
862     global zigbeeLongShortAddr
863     global zigbeeLongShortAddrMutex
864     global zigeeBindRequestMutex
865     global zigeeBindRequest
866     global zigbeeConnectionMutex
867     global zigbeeConnection
868     global matchDescriptorReqSingleton
869     shortAddr = None
870
871     # get the short address for this device long address if possible
872     zigbeeLongShortAddrMutex.acquire()
873     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
874         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
875     zigbeeLongShortAddrMutex.release()
876
877
878     # if there is a short address than we can send the message
879     # if there is not one then we cannot since we need both the short and
880     # the long address
881     if(shortAddr != None):
882
883         # get a request number
884         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
885         
886         # send back failure
887         if(seqNumber == -1):
888
889             # send an error message, could not get a sequence number to use at this time
890             sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
891             return
892
893         # get the info for sending
894         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
895         destShortAddr = hexStringToZigbeeHexString(shortAddr)
896         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
897
898         # create the payload data
899         payloadData = ""
900         payloadData += chr(seqNumber)
901         payloadData += '\x5a'
902         payloadData += '\x00'
903
904         # create and send binding command
905         zigbeeConnectionMutex.acquire()
906         zigbeeConnection.send('tx_explicit',
907                             frame_id='\x01',
908                             # frame_id=chr(seqNumber),
909                             dest_addr_long=destLongAddr,
910                             dest_addr=destShortAddr,
911                             src_endpoint='\x00',
912                             dest_endpoint='\x00',
913                             cluster=clusterId,  
914                             profile='\x00\x00',
915                             data=payloadData
916                             )
917         print '> ManagementPermitJoiningReq is sent'
918
919         #stop answering 0x6
920         matchDescriptorReqSingleton= False
921         zigbeeConnectionMutex.release()
922
923
924     else:
925         # send a fail response
926         sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
927         pass
928
929
930 def processUdpZclReadAttributesMessage(parsedData, addr):
931     ''' Method handle a ZCL read attribute command
932
933         parsedData -- Pre-parsed Data that was in the UDP packet.
934         addr -- Address (IP and Port) of the UDP packet origin.
935     '''
936
937     global zigbeeLongShortAddr
938     global zigbeeLongShortAddrMutex
939     global zigeeBindRequestMutex
940     global zigeeBindRequest
941     global zigbeeConnectionMutex
942     global zigbeeConnection
943
944
945
946     if(zigbeeAddressAuthorityDict.has_key(addr)):
947         l = zigbeeAddressAuthorityDict[addr]
948         if(parsedData['device_address_long'] not in l):
949             return
950     else:
951         return
952
953
954     shortAddr = None
955
956     # get the short address for this device long address if possible
957     zigbeeLongShortAddrMutex.acquire()
958     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
959         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
960     zigbeeLongShortAddrMutex.release()
961
962
963     # if there is a short address than we can send the message
964     # if there is not one then we cannot since we need both the short and
965     # the long address
966     if(shortAddr != None):
967
968         # get a request number
969         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
970         
971         # send back failure
972         if(seqNumber == -1):
973
974             # send an error message, could not get a sequence number to use at this time
975             sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
976             return
977
978         # get the info for sending
979         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
980         destShortAddr = hexStringToZigbeeHexString(shortAddr)
981         profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
982         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
983         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
984
985         # get all the attributes
986         attributeIds = parsedData['attribute_ids'].split(',')
987
988         # create the payload data
989         payloadData = ""
990         payloadData += '\x00'
991         payloadData += chr(seqNumber)
992         payloadData += '\x00'
993
994         # make all the attributes payloads
995         for attr in attributeIds:
996             attr = attr.strip()
997             attr = changeEndian(attr)
998             payloadData += hexStringToZigbeeHexString(attr)
999
1000         # create and send binding command
1001         zigbeeConnectionMutex.acquire()
1002         zigbeeConnection.send('tx_explicit',
1003                             frame_id='\x01',
1004                             # frame_id=chr(seqNumber),
1005                             dest_addr_long=destLongAddr,
1006                             dest_addr=destShortAddr,
1007                             src_endpoint='\x00',
1008                             dest_endpoint=dstEndpoint,
1009                             cluster=clusterId,  
1010                             profile=profileId,
1011                             data=payloadData
1012                             )
1013         zigbeeConnectionMutex.release()
1014
1015
1016     else:
1017         # send a fail response
1018         sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1019         pass
1020
1021 def processUdpZclConfigureReportingMessage(parsedData, addr):
1022     ''' Method handle a zcl configure reporting message
1023
1024         parsedData -- Pre-parsed Data that was in the UDP packet.
1025         addr -- Address (IP and Port) of the UDP packet origin.
1026     '''
1027
1028     global zigbeeLongShortAddr
1029     global zigbeeLongShortAddrMutex
1030     global zigeeBindRequestMutex
1031     global zigeeBindRequest
1032     global zigbeeConnectionMutex
1033     global zigbeeConnection
1034
1035     if(zigbeeAddressAuthorityDict.has_key(addr)):
1036         l = zigbeeAddressAuthorityDict[addr]
1037         if(parsedData['device_address_long'] not in l):
1038             return
1039     else:
1040         return
1041
1042
1043     shortAddr = None
1044
1045     # get the short address for this device long address if possible
1046     zigbeeLongShortAddrMutex.acquire()
1047     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1048         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1049     zigbeeLongShortAddrMutex.release()
1050
1051     # if there is a short address than we can send the message
1052     # if there is not one then we cannot since we need both the short and
1053     # the long address
1054     if(shortAddr != None):
1055
1056         # get a request number
1057         seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1058         
1059         # send back failure
1060         if(seqNumber == -1):
1061             sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1062             return
1063
1064         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1065         destShortAddr = hexStringToZigbeeHexString(shortAddr)
1066         profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1067         clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1068         dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1069
1070         # create the payload data
1071         payloadData = ""
1072         payloadData += '\x00'
1073         payloadData += chr(seqNumber)
1074         payloadData += '\x06'
1075         payloadData += '\x00'
1076         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1077         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1078         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1079         payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1080
1081         if(parsedData.has_key('reportable_change')):
1082             payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1083
1084
1085         # create and send binding command
1086         zigbeeConnectionMutex.acquire()
1087         zigbeeConnection.send('tx_explicit',
1088                             frame_id='\x01',
1089                             # frame_id=chr(seqNumber),
1090                             dest_addr_long=destLongAddr,
1091                             dest_addr=destShortAddr,
1092                             src_endpoint='\x00',
1093                             dest_endpoint=dstEndpoint,
1094                             cluster=clusterId,  
1095                             profile=profileId,
1096                             data=payloadData
1097                             )
1098         zigbeeConnectionMutex.release()
1099
1100
1101     else:
1102         sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1103         pass
1104
1105
1106 def processUdpPolicySet(parsedData, addr):
1107     ''' Method handle a policy set message
1108
1109         parsedData -- Pre-parsed Data that was in the UDP packet.
1110         addr -- Address (IP and Port) of the UDP packet origin.
1111     '''
1112     print "=================================================================="
1113     print "Policy set: ", parsedData
1114     print 'addr : ', addr
1115
1116
1117     # do nothing if wrong source
1118     #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 :
1119     #if addr == SYSTEM_MASTER_ADDRESS :
1120     if addr[0] == SYSTEM_MASTER_ADDRESS[0]:
1121         key = (parsedData['ip_address'], int(parsedData['port']))
1122         if (zigbeeAddressAuthorityDict.has_key(key)):
1123             zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1124         else:
1125             zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1126
1127
1128 def processUdpPolicyClear(parsedData, addr):
1129     ''' Method handle a policy set message
1130
1131         parsedData -- Pre-parsed Data that was in the UDP packet.
1132         addr -- Address (IP and Port) of the UDP packet origin.
1133     '''
1134     print "=================================================================="
1135     print "Clear policy: ", parsedData
1136     
1137     # do nothing if wrong source
1138     #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1139     if addr == SYSTEM_MASTER_ADDRESS :
1140         zigbeeAddressAuthorityDict.clear()
1141
1142
1143 # -------------
1144 # Zigbee 
1145 # -------------
1146
1147 def processZigbeeATCommandMessage(parsedData):
1148     ''' Method to process an AT zigbee message
1149
1150         parsedData -- Pre-parsed (into a dict) data from message.
1151     '''
1152     global ZIGBEE_DEVICE_ADDRESS
1153     global didGetLocalRadioHighAddress
1154     global didGetLocalRadioLowAddress
1155
1156     # command response for the high bytes of the local device long address
1157     if(parsedData['command'] == 'SH'):
1158         # convert the parameter to a string value (human readable)
1159         value = ""
1160         for e in parsedData['parameter']:
1161             value += "{0:02x}".format(ord(e))
1162
1163         # set the correct portion of the address
1164         ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1165         
1166         #signal that we got this part of the address
1167         didGetLocalRadioHighAddress = True
1168
1169     # command response for the low bytes of the local device long address
1170     elif(parsedData['command'] == 'SL'):
1171         # convert the parameter to a string value (human readable)
1172         value = ""
1173         for e in parsedData['parameter']:
1174             value += "{0:02x}".format(ord(e))
1175
1176         # set the correct portion of the address
1177         ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1178
1179         #signal that we got this part of the address
1180         didGetLocalRadioLowAddress = True
1181
1182 def processZigbeeRxExplicitCommandMessage(parsedData):
1183     ''' Method to process a rx-explicit zigbee message
1184
1185         parsedData -- Pre-parsed (into a dict) data from message.
1186     '''
1187     global zigeeBindRequestMutex
1188     global zigeeBindRequest
1189     global zigbeeConnectionMutex
1190     global zigbeeConnection
1191     global ManagementPermitJoiningReqSuccess
1192
1193     # get the long and short addresses from the message payload since we can 
1194     # use these to update the short addresses since this short address is fresh
1195     longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1196     shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1197
1198     # check if this short address is for a device that has yet to be 
1199     # registered
1200     zigbeeUnregisteredAddressesMutex.acquire()
1201     if(longAddr in zigbeeUnregisteredAddresses):
1202         zigbeeUnregisteredAddresses.remove(longAddr)
1203     zigbeeUnregisteredAddressesMutex.release()
1204
1205     # update/ or insert the short address
1206     zigbeeLongShortAddrMutex.acquire()
1207     zigbeeLongShortAddr[longAddr] = shortAddr
1208     zigbeeLongShortAddrMutex.release()
1209
1210     global matchDescriptorReqSingleton
1211     global deviceAnnouncementSingleton
1212     global seqNumberForNotification
1213
1214     # if this is a ZDO message/response
1215     #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1216     #print parsedData
1217     #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1218     if(parsedData['profile'] == '\x00\x00'):
1219
1220         # made by changwoo
1221         # if this is a Match Descriptor Request so we need to answer.
1222         if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1223             zigbeeConnectionMutex.acquire()
1224             zigbeeConnection.send('tx_explicit',
1225                             frame_id='\x08',
1226                             # frame_id=chr(seqNumber),
1227                             dest_addr_long=parsedData['source_addr_long'],
1228                             dest_addr=parsedData['source_addr'],
1229                             src_endpoint='\x00',
1230                             dest_endpoint='\x00',
1231                             cluster='\x00\x06',
1232                             profile='\x00\x00',
1233                             data=parsedData['rf_data']
1234                             )
1235             time.sleep(1)
1236             zigbeeConnection.send('tx_explicit',
1237                             frame_id='\x40',
1238                             # frame_id=chr(seqNumber),
1239                             dest_addr_long=parsedData['source_addr_long'],
1240                             dest_addr=parsedData['source_addr'],
1241                             src_endpoint='\x00',
1242                             dest_endpoint='\x00',
1243                             cluster='\x80\x06',
1244                             profile='\x00\x00',
1245                             data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1246                             )
1247             time.sleep(1)
1248             print ''
1249             print '[ 0x0006 ] Match Descriptor Request - answered'
1250             print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1251             zigbeeConnectionMutex.release()
1252
1253
1254         # if this is a device announcement so we can get some useful data from it
1255         elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1256             #print parsedData
1257             # pick out the correct parts of the payload
1258             longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1259             shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1260
1261             # change the endian of the address
1262             longAddr = changeEndian(longAddr)
1263             shortAddr = changeEndian(shortAddr)
1264
1265             # update the table with the new information
1266             zigbeeLongShortAddrMutex.acquire()
1267             zigbeeLongShortAddr[longAddr] = shortAddr
1268             zigbeeLongShortAddrMutex.release()
1269
1270             # check if this short address is for a device that has yet to be 
1271             # registered
1272             zigbeeUnregisteredAddressesMutex.acquire()
1273             if(longAddr in zigbeeUnregisteredAddresses):
1274                 zigbeeUnregisteredAddresses.remove(longAddr)
1275             zigbeeUnregisteredAddressesMutex.release()
1276
1277
1278             # made by changwoo
1279             zigbeeConnectionMutex.acquire()
1280             zigbeeConnection.send('tx_explicit',
1281                             frame_id='\x08',
1282                             # frame_id=chr(seqNumber),
1283                             dest_addr_long=parsedData['source_addr_long'],
1284                             dest_addr=parsedData['source_addr'],
1285                             src_endpoint='\x00',
1286                             dest_endpoint='\x00',
1287                             cluster='\x00\x13',
1288                             profile='\x00\x00',
1289                             data=parsedData['rf_data']
1290                             )
1291             print ''
1292             print '[ 0x0013 ] device announcement - answered'
1293             print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1294             deviceAnnouncementSingleton = False
1295             zigbeeConnectionMutex.release()
1296
1297
1298         # if this is a response to a zdo bind_req message
1299         elif(parsedData['cluster'] == '\x80\x21'):
1300
1301             # get the status and sequence number from the message
1302             seqNumber = parsedData['rf_data'][0]
1303             statusCode = parsedData['rf_data'][1]
1304             print ">response to a zdo bind_req message parsedData>"
1305
1306             # get the bind tuple information
1307             # for this specific bind request
1308             tup = None
1309             zigeeBindRequestMutex.acquire() 
1310             if(zigeeBindRequest.has_key(ord(seqNumber))):
1311                 tup = zigeeBindRequest[ord(seqNumber)]
1312             zigeeBindRequestMutex.release()
1313
1314             if(tup == None):
1315                 # cant really do anything in this case...
1316                 # don't have any information on who the data is for
1317                 return
1318
1319             # successful binding
1320             if(ord(statusCode) == 0):
1321
1322                 # add a callback for this specific device and cluster 
1323                 # to the HA callback dict 
1324                 zibeeHACallbackMutex.acquire();
1325                 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1326                     if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1327                         zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1328                 else:
1329                     zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1330                 zibeeHACallbackMutex.release()
1331
1332                 # send success message
1333                 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1334
1335             # Not Supported
1336             elif (ord(statusCode) == 170):
1337                 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1338
1339             # Table Full
1340             elif (ord(statusCode) == 174):
1341                 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1342
1343             # Other issue, dont have code for
1344             else:
1345                 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1346
1347         # if this is a response to a short address query
1348         elif(parsedData['cluster'] == '\x80\x00'):
1349             print ">response to a short address query 0x8000"
1350             
1351             # get a status code
1352             statusCode = parsedData['rf_data'][0]
1353
1354             # does not matter if this is not a success, we can try again later
1355             if(statusCode != '\x00'):
1356                 # status code was not success so do not do anything
1357                 return
1358
1359             # get the short and long address information
1360             longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1361             shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1362
1363             # remove device from list of unregistered devices if it is in it
1364             zigbeeUnregisteredAddressesMutex.acquire()
1365             if(longAddr in zigbeeUnregisteredAddresses):
1366                 zigbeeUnregisteredAddresses.remove(longAddr)
1367             zigbeeUnregisteredAddressesMutex.release()
1368
1369             # update/insert the short address
1370             zigbeeLongShortAddrMutex.acquire()
1371             zigbeeLongShortAddr[longAddr] = shortAddr
1372             zigbeeLongShortAddrMutex.release()
1373
1374         #made by changwoo
1375         elif(parsedData['cluster'] == '\x80\x06'):
1376             print ''
1377             print '[ 0x8006 ] get Match Descriptor Response'
1378             print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1379
1380         #made by changwoo
1381         elif(parsedData['cluster'] == '\x80\x36'):
1382             print ''
1383             print '[ 0x8036 ] get Management Permit Joining Response'
1384             print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1385
1386             ManagementPermitJoiningReqSuccess = True
1387
1388         #made by changwoo
1389         else :
1390             print ''
1391             print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1392             print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1393
1394
1395     # if this is a home automation zcl message/response
1396     elif (parsedData['profile'] == '\x01\x04'):
1397
1398         # get the zcl message header
1399         zclFrameControl = parsedData['rf_data'][0]
1400         zclSeqNumber = parsedData['rf_data'][1]
1401         zclCommand = parsedData['rf_data'][2]
1402         zclStatus = parsedData['rf_data'][3]
1403
1404         #made by changwoo
1405         if(zclCommand == '\x00'):
1406             print ''
1407             print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1408             
1409             # find who to send response 
1410             tup = None
1411             zigbeeSeqNumberToClientMutex.acquire()
1412
1413             if(longAddr in seqNumberForNotification):
1414                 key = longAddr
1415                 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1416                     tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1417                     #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1418             zigbeeSeqNumberToClientMutex.release()
1419
1420             # no one to send the response to so just move on
1421             if(tup == None):
1422                 # cant really do anything here
1423                 return
1424             # create the response message
1425             packetId = tup[2]
1426             message = "type : zcl_zone_status_change_notification\n"
1427             message += "packet_id: " + packetId + "\n"
1428             message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1429             message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1430             message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1431             message += "attributes: success"
1432             message += "\n"
1433             # send the socket
1434             sendSoceket.sendto(message,tup[0])
1435             print(">port : ", tup[0][1])
1436
1437
1438
1439         # this is a zcl read attribute response
1440         elif(zclCommand == '\x01'):
1441
1442             # get the zcl payload
1443             zclPayload = parsedData['rf_data'][3:]
1444             attibuteResponseList = []
1445
1446             # get the data for each data
1447             while(len(zclPayload) > 0):
1448                 attributeId = zclPayload[0:2]
1449                 attributeStatus = zclPayload[2]
1450                 zclPayload = zclPayload[3:]
1451                 
1452                 if(ord(attributeStatus) != 0):
1453                     # if attribute is not supported then it has no data
1454                     # package the data and add it to the list
1455                     attibuteResponseList.append((attributeId,"not_supported"))
1456                 else:
1457
1458                     # get the data type and data length of the attributre
1459                     attributeType = zclPayload[0]
1460                     dataLength = zclDataTypeToBytes(zclPayload)
1461
1462                     # consume zcl payload data
1463                     if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1464                         zclPayload = zclPayload[2:]
1465                     elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1466                         zclPayload = zclPayload[3:]
1467                     else:
1468                         zclPayload = zclPayload[1:]
1469
1470                     # package the data and add it to the list
1471                     newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1472                     attibuteResponseList.append(newData)
1473
1474                     # consume the data size of the payload
1475                     zclPayload = zclPayload[dataLength:]
1476
1477             # find who to send response to 
1478             tup = None
1479             zigbeeSeqNumberToClientMutex.acquire()
1480             if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1481                 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1482                 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1483             zigbeeSeqNumberToClientMutex.release()
1484
1485             # no one to send the response to so just move on
1486             if(tup == None):
1487                 # cant really do anything here
1488                 return
1489             
1490             # create the response message
1491             packetId = tup[2]
1492             message = "type : zcl_read_attributes_response \n"
1493             message += "packet_id: " + packetId + "\n"
1494             message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1495             message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1496             message += "attributes: " 
1497
1498             # create the message for each attribute
1499             for t in attibuteResponseList:
1500                 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1501                 if(t[1] == "success"):
1502                     attrIdStr = "%0.4x" % attrId
1503                     attrIdStr = changeEndian(attrIdStr)
1504
1505                     message += attrIdStr
1506                     message += ", "
1507                     message +=  "success"
1508                     message += ", "
1509                     message += "%0.2x" % ord(t[2])
1510                     message += ", "
1511
1512                     dat = ""
1513                     for c in (t[3]):
1514                         dat += "%0.2x" % ord(c)
1515                     dat = changeEndian(dat)
1516                     message += dat
1517                     message += ";"
1518                 else:
1519                     attrIdStr = "%0.4x" % attrId
1520                     attrIdStr = changeEndian(attrIdStr)
1521
1522                     message += attrIdStr
1523                     message += ", "
1524                     message +=  "not_supported"
1525                     message += ";"
1526
1527             message = message[0:len(message) - 1]
1528             message += "\n"
1529             # send the socket
1530             sendSoceket.sendto(message,tup[0])
1531
1532
1533
1534
1535         # made by changwoo
1536         # this is a zcl write attribute response
1537         elif(zclCommand == '\x04'):
1538
1539             # get the zcl payload
1540             zclPayload = parsedData['rf_data'][3]
1541             # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1542             if(zclStatus == '\x70' or zclPayload == '\x00'):
1543
1544                 # find who to send response to 
1545                 tup = None
1546                 zigbeeSeqNumberToClientMutex.acquire()
1547                 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1548                     tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1549                     seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1550                     #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1551                 zigbeeSeqNumberToClientMutex.release()
1552                 # no one to send the response to so just move on
1553                 if(tup == None):
1554                     # cant really do anything here
1555                     return
1556             
1557                 # create the response message
1558                 packetId = tup[2]
1559                 message = "type : zcl_write_attributes_response\n"
1560                 message += "packet_id: " + packetId + "\n"
1561                 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1562                 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1563                 message += "attributes: success"
1564                 message += "\n"
1565                 # send the socket
1566                 sendSoceket.sendto(message,tup[0])
1567                 print ''
1568                 print '[ 0x0500 ] get Write Attribute Response success'
1569                 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1570
1571             else:
1572                 print ''
1573                 print '[ 0x0500 ] get Write Attribute Response'
1574                 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1575
1576
1577
1578         # this is a zcl configure attribute response
1579         elif(zclCommand == '\x07'):
1580
1581             # find who to send response to 
1582             tup = None
1583             zigbeeSeqNumberToClientMutex.acquire()
1584             if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1585                 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1586                 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1587             zigbeeSeqNumberToClientMutex.release()
1588
1589             # no one to send the response to so just move on
1590             if(tup == None):
1591                 # cant really do anything here
1592                 return
1593
1594             # get zcl payload
1595             zclPayload = parsedData['rf_data'][3:]
1596             
1597             # construct the message
1598             packetId = tup[2]
1599             message = "type : zcl_configure_reporting_response \n"
1600             message += "packet_id: " + packetId + "\n"
1601             message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1602             message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1603             message += "attributes: " 
1604
1605             if(len(zclPayload) == 1):
1606                 # if all the configurations are a success then only send back a success
1607                 # based on zigbee specs
1608                 message +=  "all_success \n";
1609                 sendSoceket.sendto(message,tup[0])
1610             
1611             else:
1612                 attibuteResponseList = []
1613                 
1614                 # get each attributes data
1615                 while(len(zclPayload) > 0):
1616                     attributeStatus = zclPayload[0]
1617                     attributeDirection = zclPayload[1]
1618                     attributeId = zclPayload[2:4]
1619                     zclPayload = zclPayload[4:]
1620
1621                     newData = (attributeStatus,attributeDirection, attributeId)
1622                     attibuteResponseList.append(newData)
1623
1624                 # package each attribute 
1625                 for t in attibuteResponseList:
1626                     attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1627                     attrIdStr = "%0.4x" % attrId
1628                     attrIdStr = changeEndian(attrIdStr)
1629
1630                     message += attrIdStr
1631                     message += ", "
1632                     if(ord(t[0]) == 0):
1633                         message +=  "success"
1634                     else:
1635                         message +=  "error"
1636
1637                     message += ", "
1638
1639                     if(ord(t[1]) == 0):
1640                         message +=  "reported"
1641                     else:
1642                         message +=  "received"
1643                     message += ";"
1644
1645                 message = message[0:len(message) - 1]
1646                 message += "\n"
1647                 sendSoceket.sendto(message,tup[0])
1648
1649         # this is a zcl report attribute message
1650         elif(zclCommand == '\x0a'):
1651             print "get Report attribute "
1652             # get teh zcl payload
1653             zclPayload = parsedData['rf_data'][3:]
1654             attibuteResponseList = []
1655  
1656             # extract the attribute data
1657             while(len(zclPayload) > 0):
1658                 attributeId = zclPayload[0:2]
1659                 zclPayload = zclPayload[2:]
1660                 attributeType = zclPayload[0]
1661                 dataLength = zclDataTypeToBytes(zclPayload)
1662
1663                 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1664                     zclPayload = zclPayload[2:]
1665                 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1666                     zclPayload = zclPayload[3:]
1667                 else:
1668                     zclPayload = zclPayload[1:]
1669
1670                 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1671                 attibuteResponseList.append(newData)
1672                 zclPayload = zclPayload[dataLength:]
1673
1674
1675             # get callback clients to respond to
1676             callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1677             retAddr = None
1678             zibeeHACallbackMutex.acquire()
1679             if(zibeeHACallback.has_key(callbackIndex)):
1680                 retAddr = zibeeHACallback[callbackIndex]
1681             zibeeHACallbackMutex.release()
1682
1683             # no one to respond to so do nothing here
1684             if(retAddr == None):
1685                 return
1686
1687             # construct the message
1688             message = "type : zcl_report_attributes \n"
1689             message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1690             message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1691             message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1692             message += "attributes: " 
1693
1694             # package the attributes
1695             for t in attibuteResponseList:
1696                 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1697                 attrIdStr = "%0.4x" % attrId
1698                 attrIdStr = changeEndian(attrIdStr)
1699
1700                 message += attrIdStr
1701                 message += ", "
1702                 message += "%0.2x" % ord(t[1])
1703                 message += ", "
1704
1705                 dat = ""
1706                 for c in (t[2]):
1707                     dat += "%0.2x" % ord(c)
1708                 dat = changeEndian(dat)
1709                 message += dat
1710                 message += ";"
1711
1712             message = message[0:len(message) - 1]
1713             message += "\n"
1714             print "Sending", message
1715             
1716             # send to all client that want this callback
1717             for ra in retAddr:
1718                 sendSoceket.sendto(message,ra)
1719
1720 # -----------------------------------------------------------------------------
1721 # Communication Callback/Parse Methods
1722 # -----------------------------------------------------------------------------
1723 def handleNewZigbeeMessage(parsedData):
1724     ''' Method to process a zigbee message from the local radio.
1725
1726         parsedData -- Pre-parsed (into a dict) data from message.
1727     '''
1728     #print "=================================================================="
1729     #print ''
1730     print "New Zigbee Message"
1731     #printMessageData(parsedData)
1732
1733     # dispatch to the correct zigbee handler
1734     if (parsedData['id'] == 'at_response'):
1735         print "parsedDataID : at_response"
1736         processZigbeeATCommandMessage(parsedData)
1737
1738     elif (parsedData['id'] == 'rx_explicit'):
1739         print "parsedDataID : rx_explicit"
1740         processZigbeeRxExplicitCommandMessage(parsedData)
1741
1742     else:
1743         print "Unknown API format"
1744
1745     #print "=================================================================="
1746
1747 def handleNewUdpPacket(data, addr):
1748     ''' Method to parse and handle an incoming UDP packet.
1749
1750         data -- Data that was in the UDP packet.
1751         addr -- Address (IP and Port) of the UDP packet origin.
1752     '''
1753     global ManagementPermitJoiningReqSuccess
1754
1755     #print "=================================================================="
1756     #print ''
1757     #print "Got New UDP packet..."
1758     #print data
1759
1760
1761     # data comes in as 'key: value\n key: value...' string and so needs to be
1762     # parsed into a dict
1763     parsedData = dict()
1764
1765     # 1 key, value pair per line
1766     for line in data.split('\n'):
1767
1768         # key and values are split based on a ':'
1769         fields = line.split(':')
1770
1771         # make sure properly formated otherwise just ignore it
1772         if len(fields) == 2:
1773
1774             # do strips to remove any white spacing that may have resulted
1775             # from improper packing on the sender side
1776             parsedData[fields[0].strip()] = fields[1].strip()
1777
1778
1779     # wrap in try statement just in case there is an improperly formated packet we
1780     # can deal with it
1781     try:
1782         # dispatch to the correct process method
1783         if(parsedData["type"] == "zdo_bind_request"):
1784             print "> processUdpZdoBindReqMessage call"
1785             processUdpZdoBindReqMessage(parsedData, addr)
1786         elif(parsedData["type"] == "zdo_unbind_request"):
1787             processUdpZdoUnBindReqMessage(parsedData, addr)
1788         elif(parsedData["type"] == "send_address"):
1789             print "> processUdpSendAddressMessage call"
1790             processUdpSendAddressMessage(parsedData, addr)
1791         elif(parsedData["type"] == "zcl_read_attributes"):
1792             processUdpZclReadAttributesMessage(parsedData, addr)
1793         elif(parsedData["type"] == "zcl_configure_reporting"):
1794             print "> zcl_configure_reporting call"
1795             processUdpZclConfigureReportingMessage(parsedData, addr)
1796         elif(parsedData["type"] == "policy_set"):
1797             processUdpPolicySet(parsedData, addr)
1798         elif(parsedData["type"] == "policy_clear"):
1799             processUdpPolicyClear(parsedData, addr)
1800         elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo
1801             processUdpManagementPermitJoiningReqMessage(parsedData, addr)
1802         elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo
1803             processUdpZclWriteAttributesMessage(parsedData, addr)
1804         elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo
1805             processUdpEnrollmentResponse(parsedData, addr)
1806         elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo
1807             processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
1808         elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo
1809             processUdpZclChangeSwitchReqMessage(parsedData, addr)
1810         else:
1811             print "unknown Packet: " + parsedData["type"]
1812             pass
1813     except:
1814         # if we ever get here then something went wrong and so just ignore this
1815         # packet and try again later
1816         print "I didn't expect this error:", sys.exc_info()[0]
1817         traceback.print_exc()
1818
1819     #print "=================================================================="
1820
1821
1822 # -----------------------------------------------------------------------------
1823 # Main Running Methods
1824 # -----------------------------------------------------------------------------
1825
1826 def main():
1827     '''Main function used for starting the application as the main driver'''
1828
1829     global ZIGBEE_SERIAL_PORT
1830     global ZIGBEE_SERIAL_BAUD
1831     global UDP_RECEIVE_PORT
1832     global zigbeeConnection
1833     global zigbeeMutex
1834     global doEndFlag
1835
1836     parseCommandLineArgs(sys.argv[1:])
1837
1838     # create serial object used for communication to the zigbee radio
1839     sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
1840
1841     # create a zigbee object that handles all zigbee communication
1842     # we use this to do all communication to and from the radio
1843     # when data comes from the radio it will get a bit of unpacking
1844     # and then a call to the callback specified will be done with the
1845     # unpacked data
1846     zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
1847
1848     # get the long address of our local radio before we start doing anything
1849     getConnectedRadioLongAddress();
1850
1851     # setup incoming UDP socket and bind it to self and specified UDP port
1852     # sending socket does not need to be bound to anything
1853     #receiveSoceket.bind(('192.168.2.227', UDP_RECEIVE_PORT))
1854     receiveSoceket.bind(('192.168.2.192', UDP_RECEIVE_PORT))
1855
1856     # create the thread that does short address lookups
1857     addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
1858     addressUpdateWorkerThread.start()
1859
1860     try:
1861         # Main running loop
1862         while(True):
1863             print "=================================================================="
1864             print ''
1865             print "Waiting..."
1866             print "=================================================================="
1867
1868             # wait for an incoming UDP packet
1869             # this is a blocking call
1870             data, addr = receiveSoceket.recvfrom(4096)
1871
1872             # handle the UDP packet appropriately
1873             handleNewUdpPacket(data, addr)
1874
1875     except KeyboardInterrupt:
1876         # use the keyboard interupt to catch a ctrl-c and kill the application
1877         pass
1878
1879     except:
1880         # something went really wrong and so exit with error message
1881         traceback.print_exc()
1882
1883     # signal all threads to exit
1884     doEndFlag = True
1885
1886     # wait for threads to finish before closing of the resources
1887     addressUpdateWorkerThread.join()
1888
1889
1890     # make sure to close all the connections
1891     zigbeeConnection.halt()
1892     receiveSoceket.close()
1893     sendSoceket.close()
1894
1895 if __name__ == "__main__":
1896     # call main function since this is being run as the start
1897     main()