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