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