Adjusting and cleaning up ZigbeeTest to install Vigilia ZigBee gateway and devices.
[iot2.git] / benchmarks / other / XbeePythonDriver / xbee_driver.py
index 84e7a0b0ac3ac63351dcda7132599be0a5be5c1c..488effe3ee358d411d00135a33f1bbc06a402c58 100644 (file)
@@ -1,3 +1,10 @@
+# -----------------------------------------------------------------------------
+# ZigBee Gateway Driver for Vigilia v.1.0
+# Created by Ali Younis
+# Modified by Rahmadi Trimananda, Lee Changwoo, Jiawei
+# (c) 2016-2018 University of California, Irvine
+# -----------------------------------------------------------------------------
+
 from xbee import ZigBee
 import serial
 import time
@@ -16,13 +23,15 @@ import threading
 # -----------------------------------------------------------------------------
 UDP_RECEIVE_PORT = 5005        # port used for incoming UDP data
 UDP_RECEIVE_BUFFER_SIZE = 4096  # max buffer size of an incoming UDP packet
-SYSTEM_MASTER_ADDRESS = ("192.168.2.108", 12345) # ip address and portof the system master node computer ip addr running java
+SYSTEM_MASTER_ADDRESS = ("192.168.1.198", 12345) # ip address and portof the system master node
+LOCAL_ADDRESS = "192.168.1.192" # local IP address
 
 # time for messages to wait for a response before the system clears away that 
 # sequence identifier
 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5 
 
-ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DJIP"  # USB-Serial port of local radio
+#ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH"  # USB-Serial port of local radio
+ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
 ZIGBEE_SERIAL_BAUD = 115200                       # Baud rate for above port
 
 # address of our local zigbee radio
@@ -40,6 +49,11 @@ didGetLocalRadioLowAddress = False;
 zigbeeConnection = None
 zigbeeConnectionMutex = Lock()
 
+#singleton mabe by Changwoo
+matchDescriptorReqSingleton = True
+deviceAnnouncementSingleton = True
+ManagementPermitJoiningReqSuccess = False
+
 # zigbee mapping from long to short object dict
 zigbeeLongShortAddr = dict()
 zigbeeLongShortAddrMutex = Lock()
@@ -49,12 +63,12 @@ zigbeeLongShortAddrMutex = Lock()
 zigbeeSeqNumberToClient = dict()
 zigbeeSeqNumberToClientMutex = Lock()
 
-zigeeBindRequest = dict()
-zigeeBindRequestMutex = Lock()
+zigbeeBindRequest = dict()
+zigbeeBindRequestMutex = Lock()
 
 # Keeps record of where to send callbacks to when an HA message is received
 zibeeHACallback = dict()
-zibeeHACallbackMutex = Lock()
+zigbeeHACallbackMutex = Lock()
 
 
 # Keeps a record of device addresses whose short addresses have not been 
@@ -69,17 +83,22 @@ doEndFlag = False
 # 2 sockets, one for sending (not bound to a port manually)
 # and one for receiving, known port binding by application
 # both UDP sockets
-sendSoceket = socket(AF_INET, SOCK_DGRAM)
-receiveSoceket = socket(AF_INET, SOCK_DGRAM)
+sendSocket = socket(AF_INET, SOCK_DGRAM)
+receiveSocket = socket(AF_INET, SOCK_DGRAM)
 
 
 # zigbee address authority list
 zigbeeAddressAuthorityDict = dict()
 
+# Added by Changwoo
+seqNumberForNotification = dict()
+
 # -----------------------------------------------------------------------------
 # Helper Methods
 # -----------------------------------------------------------------------------
-
+def reverseShortAddress(shortAddr):
+    result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
+    return result
 
 def parseCommandLineArgs(argv):
     global ZIGBEE_SERIAL_PORT
@@ -397,6 +416,7 @@ def addressUpdateWorkerMethod():
 
             # create and send binding command
             zigbeeConnectionMutex.acquire()
+           
             zigbeeConnection.send('tx_explicit',
                                 frame_id='\x01',
                                 dest_addr_long=hexStringToZigbeeHexString(ad),
@@ -409,56 +429,594 @@ def addressUpdateWorkerMethod():
                                 )
             zigbeeConnectionMutex.release()
 
-        time.sleep(1)
+        time.sleep(1)
+
+
+# -------------
+# UDP 
+# -------------
+
+def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
+    ''' Method to send a success or fail back to a client.
+
+        addr -- UDP address to send packet to
+        packetTypeStr -- name of this specific packet
+        packetIdStr -- packet id to send
+        sucOrFail -- whether this is a success or fail message (True = success)
+        reason -- reason of failure (if needed, default is None)
+
+    '''
+
+    global sendSocket
+
+    # construct the message
+    message = "type: " + packetTypeStr.strip() + "\n"
+    message += "packet_id: " + packetIdStr + "\n"
+
+    if(sucOrFail):
+        message += "response: success \n"
+    else:
+        message += "response: fail \n"
+        message += "reason: " + reason + "\n"
+
+    # send message in a UDP packet
+    sendSocket.sendto(message,addr)
+
+def processUdpZdoBindReqMessage(parsedData, addr):
+
+    shortAddr = None
+
+    if(zigbeeAddressAuthorityDict.has_key(addr)):
+        l = zigbeeAddressAuthorityDict[addr]
+        if(parsedData['device_address_long'] not in l):
+            return
+    else:
+        return
+        
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+        print "> Short address exists", shortAddr
+
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+        
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # a bind request was made so must store and wait for response 
+        # before we setup callbacks, so keep just the data we need to create the callback
+        zigbeeBindRequestMutex.acquire()
+        zigbeeBindRequest[seqNumber] = (parsedData['device_address_long'],
+                                        parsedData['cluster_id'], 
+                                        parsedData['packet_id'], 
+                                        addr)
+        zigbeeBindRequestMutex.release()
+
+        # construct the short and long addresses of the message for sending
+        # make sure they are in the correct format
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+
+        # create the payload data
+        payloadData = ""
+        payloadData += chr(seqNumber)
+        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
+        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
+        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) 
+        payloadData += '\x03' 
+        payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
+        payloadData += '\x00'
+
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x01',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x00',
+                            dest_endpoint='\x00',
+                            cluster='\x00\x21',  
+                            profile='\x00\x00',
+                            data=payloadData
+                            )
+        zigbeeConnectionMutex.release()
+
+
+    else:
+        # send a failure packet since there is no short address available
+        sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
+        pass
+
+def processUdpZdoUnBindReqMessage(parsedData, addr):
+    zigbeeHACallbackMutex.acquire();
+    if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
+        zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
+    zigbeeHACallbackMutex.release()
+    sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
+
+
+
+def processUdpSendAddressMessage(parsedData, addr):
+    ''' Method handle a send address command
+
+        parsedData -- Pre-parsed Data that was in the UDP packet.
+        addr -- Address (IP and Port) of the UDP packet origin.
+    '''
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeUnregisteredAddresses
+    global zigbeeUnregisteredAddressesMutex
+    global sendSocket
+
+    print "process send address"
+    
+
+    # construct success message
+    message = "type: send_address_response\n"
+    message += "packet_id: " + parsedData['packet_id'] + "\n"
+    message += "response: success\n"
+
+    # tell client that we got their request
+    sendSocket.sendto(message,addr)
+    print "responding", message
+    
+    # construct 
+    zigbeeLongShortAddrMutex.acquire()
+    doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
+    zigbeeLongShortAddrMutex.release()
+
+    if(doesHaveKey):
+        # long address is already registered with the system so no need to do anything
+        return
+
+    # long address not registered so add it for short address lookup
+    zigbeeUnregisteredAddressesMutex.acquire()
+    zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
+    zigbeeUnregisteredAddressesMutex.release()
+
+
+
+# Added by Changwoo
+def processUdpEnrollmentResponse(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+        
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
+       profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
+
+        # create the payload data
+        payloadData = ""
+        payloadData += '\x01'
+        payloadData += chr(seqNumber)
+        payloadData += '\x00'
+        payloadData += '\x00\x00'
+
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x40',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x01',
+                            dest_endpoint=dstEndpoint,
+                            cluster=clusterId,  
+                            profile=profileId,
+                            data=payloadData
+                            )
+       print '> EnrollmentResponse is sent'
+        zigbeeConnectionMutex.release()
+
+
+    else:
+        # send a fail response
+        sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
+        pass
+
+
+
+
+# Added by Changwoo
+def processUdpZclWriteAttributesMessage(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+        
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
+       profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
+
+        # create the payload data
+        payloadData = ""
+        payloadData += '\x00'
+        payloadData += chr(seqNumber)
+        payloadData += '\x02'
+        payloadData += '\x10\x00'
+        payloadData += '\xF0'
+        payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
+
+        zigbeeConnectionMutex.acquire()
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x08',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x01',
+                            dest_endpoint=dstEndpoint,
+                            cluster=clusterId,
+                            profile=profileId,
+                            data=payloadData
+                            )
+
+       print ''
+       print '> WriteAttributesReq is sent : '+str(shortAddr)
+        zigbeeConnectionMutex.release()
+
+
+    else:
+        # send a fail response
+        sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
+        pass
+
+# Added by Changwoo
+def processUdpZclChangeSwitchReqMessage(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
+       clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
+       profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
+       value = hexStringToZigbeeHexString(parsedData['value'])
+
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x40',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x01',
+                            dest_endpoint=dstEndpoint,
+                            cluster=clusterId,  
+                            profile=profileId,
+                            data='\x01'+chr(seqNumber)+value
+                            )
+        time.sleep(1)
+       if parsedData['value']==1:
+               print '> The outlet sensor turned on'
+       else :
+               print '> The outlet sensor turned off'
+
+        zigbeeConnectionMutex.release()
+
+
+    else:
+        # send a fail response
+        sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
+        pass
+
+
+# Added by Jiawei
+def processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
+        clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
+        profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
+        value = hexStringToZigbeeHexString(parsedData['value'])
+
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x40',
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x01',
+                            dest_endpoint=dstEndpoint,
+                            cluster=clusterId,  
+                            profile=profileId,
+                            data='\x01'+chr(seqNumber)+value
+                            )
+        time.sleep(1)
+        if value == '\x01':
+            print '> The door lock is unlocked'
+        elif value == '\x00':
+            print '> The door lock is locked'
+        else:
+            print '> Unknown door lock value: ' + str(value)
+
+        zigbeeConnectionMutex.release()
+
+
+    else:
+        # send a fail response
+        sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'short_address_unknown')
+
+
+# Added by Jiawei
+def processUdpZclReadDoorStatusReqMessage(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
+
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
+
+        # send back failure
+        if(seqNumber == -1):
+
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'out_of_space')
+            return
+
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
+        clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
+        profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
+
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x40',
+                            dest_addr_long=destLongAddr,
+                            dest_addr=destShortAddr,
+                            src_endpoint='\x01',
+                            dest_endpoint=dstEndpoint,
+                            cluster=clusterId,  
+                            profile=profileId,
+                            data='\x10' + chr(seqNumber) + '\x00' + '\x00\x00'
+                            )
+        time.sleep(1)
+
+        zigbeeConnectionMutex.release()
+        print "send read door status"
+
+    else:
+        # send a fail response
+        sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'short_address_unknown')
+
+
+# Added by Changwoo
+def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
+
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    shortAddr = None
+
+    # get the short address for this device long address if possible
+    zigbeeLongShortAddrMutex.acquire()
+    if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
+        shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
+    zigbeeLongShortAddrMutex.release()
+
+
+    # if there is a short address than we can send the message
+    # if there is not one then we cannot since we need both the short and
+    # the long address
+    if(shortAddr != None):
 
+        # get a request number
+        seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
 
-# -------------
-# UDP 
-# -------------
+        # send back failure
+        if(seqNumber == -1):
 
-def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
-    ''' Method to send a success or fail back to a client.
+            # send an error message, could not get a sequence number to use at this time
+            sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
+            return
 
-        addr -- UDP address to send packet to
-        packetTypeStr -- name of this specific packet
-        packetIdStr -- packet id to send
-        sucOrFail -- whether this is a success or fail message (True = success)
-        reason -- reason of failure (if needed, default is None)
+        # get the info for sending
+        destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
+        destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
 
-    '''
+        # create and send binding command
+        zigbeeConnectionMutex.acquire()
+
+        zigbeeConnection.send('tx_explicit',
+                            frame_id='\x01',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
+                            dest_addr='\xff\xfe',
+                            src_endpoint='\x00',
+                            dest_endpoint=dstEndpoint,
+                            cluster='\x00\x32',  
+                            profile='\x00\x00',
+                            data='\x12'+'\x01'
+                            )
+        time.sleep(1)
+       print '> BroadcastingRouteRecordReq is sent'
 
-    global sendSoceket
+        zigbeeConnectionMutex.release()
 
-    # construct the message
-    message = "type: " + packetTypeStr.strip() + "\n"
-    message += "packet_id: " + packetIdStr + "\n"
 
-    if(sucOrFail):
-        message += "response: success \n"
     else:
-        message += "response: fail \n"
-        message += "reason: " + reason + "\n"
+        # send a fail response
+        sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
+        pass
 
-    # send message in a UDP packet
-    sendSoceket.sendto(message,addr)
 
-def processUdpZdoBindReqMessage(parsedData, addr):
+# Added by Changwoo
+def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
 
+    global zigbeeLongShortAddr
+    global zigbeeLongShortAddrMutex
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    global matchDescriptorReqSingleton
     shortAddr = None
 
-    if(zigbeeAddressAuthorityDict.has_key(addr)):
-        l = zigbeeAddressAuthorityDict[addr]
-        if(parsedData['device_address_long'] not in l):
-            return
-    else:
-        return
-
     # get the short address for this device long address if possible
     zigbeeLongShortAddrMutex.acquire()
     if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
         shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
     zigbeeLongShortAddrMutex.release()
 
+
     # if there is a short address than we can send the message
     # if there is not one then we cannot since we need both the short and
     # the long address
@@ -471,31 +1029,18 @@ def processUdpZdoBindReqMessage(parsedData, addr):
         if(seqNumber == -1):
 
             # send an error message, could not get a sequence number to use at this time
-            sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
+            sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
             return
 
-        # a bind request was made so must store and wait for response 
-        # before we setup callbacks, so keep just the data we need to create the callback
-        zigeeBindRequestMutex.acquire()
-        zigeeBindRequest[seqNumber] = (parsedData['device_address_long'],
-                                        parsedData['cluster_id'], 
-                                        parsedData['packet_id'], 
-                                        addr)
-        zigeeBindRequestMutex.release()
-
-        # construct the short and long addresses of the message for sending
-        # make sure they are in the correct format
+        # get the info for sending
         destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
         destShortAddr = hexStringToZigbeeHexString(shortAddr)
+        clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
 
         # create the payload data
         payloadData = ""
         payloadData += chr(seqNumber)
-        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
-        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
-        payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) 
-        payloadData += '\x03' 
-        payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
+        payloadData += '\x5a'
         payloadData += '\x00'
 
         # create and send binding command
@@ -507,68 +1052,22 @@ def processUdpZdoBindReqMessage(parsedData, addr):
                             dest_addr=destShortAddr,
                             src_endpoint='\x00',
                             dest_endpoint='\x00',
-                            cluster='\x00\x21',  
+                            cluster=clusterId,  
                             profile='\x00\x00',
                             data=payloadData
                             )
+       print '> ManagementPermitJoiningReq is sent'
+
+       #stop answering 0x6
+       matchDescriptorReqSingleton= False
         zigbeeConnectionMutex.release()
 
 
     else:
-        # send a failure packet since there is no short address available
-        sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
+        # send a fail response
+        sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
         pass
 
-def processUdpZdoUnBindReqMessage(parsedData, addr):
-    zibeeHACallbackMutex.acquire();
-    if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
-        zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
-    zibeeHACallbackMutex.release()
-    sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
-
-
-
-def processUdpSendAddressMessage(parsedData, addr):
-    ''' Method handle a send address command
-
-        parsedData -- Pre-parsed Data that was in the UDP packet.
-        addr -- Address (IP and Port) of the UDP packet origin.
-    '''
-    global zigbeeLongShortAddr
-    global zigbeeLongShortAddrMutex
-    global zigbeeUnregisteredAddresses
-    global zigbeeUnregisteredAddressesMutex
-    global sendSoceket
-
-    if(zigbeeAddressAuthorityDict.has_key(addr)):
-        l = zigbeeAddressAuthorityDict[addr]
-        if(parsedData['device_address_long'] not in l):
-            return
-    else:
-        return
-
-
-    # construct success message
-    message = "type: send_address_response\n"
-    message += "packet_id: " + parsedData['packet_id'] + "\n"
-    message += "response: success\n"
-
-    # tell client that we got their request
-    sendSoceket.sendto(message,addr)
-
-    # construct 
-    zigbeeLongShortAddrMutex.acquire()
-    doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
-    zigbeeLongShortAddrMutex.release()
-
-    if(doesHaveKey):
-        # long address is already registered with the system so no need to do anything
-        return
-
-    # long address not registered so add it for short address lookup
-    zigbeeUnregisteredAddressesMutex.acquire()
-    zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
-    zigbeeUnregisteredAddressesMutex.release()
 
 def processUdpZclReadAttributesMessage(parsedData, addr):
     ''' Method handle a ZCL read attribute command
@@ -579,12 +1078,13 @@ def processUdpZclReadAttributesMessage(parsedData, addr):
 
     global zigbeeLongShortAddr
     global zigbeeLongShortAddrMutex
-    global zigeeBindRequestMutex
-    global zigeeBindRequest
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
     global zigbeeConnectionMutex
     global zigbeeConnection
 
 
+
     if(zigbeeAddressAuthorityDict.has_key(addr)):
         l = zigbeeAddressAuthorityDict[addr]
         if(parsedData['device_address_long'] not in l):
@@ -669,12 +1169,11 @@ def processUdpZclConfigureReportingMessage(parsedData, addr):
 
     global zigbeeLongShortAddr
     global zigbeeLongShortAddrMutex
-    global zigeeBindRequestMutex
-    global zigeeBindRequest
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
     global zigbeeConnectionMutex
     global zigbeeConnection
 
-
     if(zigbeeAddressAuthorityDict.has_key(addr)):
         l = zigbeeAddressAuthorityDict[addr]
         if(parsedData['device_address_long'] not in l):
@@ -682,6 +1181,7 @@ def processUdpZclConfigureReportingMessage(parsedData, addr):
     else:
         return
 
+
     shortAddr = None
 
     # get the short address for this device long address if possible
@@ -752,15 +1252,18 @@ def processUdpPolicySet(parsedData, addr):
     '''
     print "=================================================================="
     print "Policy set: ", parsedData
-    
+    print 'addr : ', addr
+
+
     # do nothing if wrong source
-    if addr == SYSTEM_MASTER_ADDRESS:
+    if addr == SYSTEM_MASTER_ADDRESS :
         key = (parsedData['ip_address'], int(parsedData['port']))
         if (zigbeeAddressAuthorityDict.has_key(key)):
             zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
         else:
             zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
 
+
 def processUdpPolicyClear(parsedData, addr):
     ''' Method handle a policy set message
 
@@ -771,9 +1274,11 @@ def processUdpPolicyClear(parsedData, addr):
     print "Clear policy: ", parsedData
     
     # do nothing if wrong source
-    if addr == SYSTEM_MASTER_ADDRESS:
+    #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
+    if addr == SYSTEM_MASTER_ADDRESS :
         zigbeeAddressAuthorityDict.clear()
 
+
 # -------------
 # Zigbee 
 # -------------
@@ -818,13 +1323,16 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
         parsedData -- Pre-parsed (into a dict) data from message.
     '''
-    global zigeeBindRequestMutex
-    global zigeeBindRequest
+    global zigbeeBindRequestMutex
+    global zigbeeBindRequest
+    global zigbeeConnectionMutex
+    global zigbeeConnection
+    global ManagementPermitJoiningReqSuccess
 
     # get the long and short addresses from the message payload since we can 
     # use these to update the short addresses since this short address is fresh
     longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
-    shortAddr = zigbeeHexStringToHexString( parsedData['source_addr'])
+    shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
 
     # check if this short address is for a device that has yet to be 
     # registered
@@ -838,13 +1346,213 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
     zigbeeLongShortAddr[longAddr] = shortAddr
     zigbeeLongShortAddrMutex.release()
 
+    global matchDescriptorReqSingleton
+    global deviceAnnouncementSingleton
+    global seqNumberForNotification
+
+    # Added by Jiawei
+    #doorlock response
+    if (parsedData['cluster'] == '\x01\x01' and parsedData['profile'] == '\x01\x04'):
+        zclSeqNumber = parsedData['rf_data'][1]
+        tup = None
+        zigbeeSeqNumberToClientMutex.acquire()
+        if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
+            tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
+            del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
+        zigbeeSeqNumberToClientMutex.release()
+
+        rfdata = parsedData['rf_data']
+        framecontrol = rfdata[0]
+        command = rfdata[2]
+
+        if tup != None:
+            packetId = tup[2]
+        
+        if framecontrol == '\x19':
+            if(command == '\x00'):
+                print ''
+                print "( 0x0101 ) Door Lock: Lock Door Response"
+                print time.strftime("%H:%M:%S", time.localtime())
+                value = rfdata[3]
+                if(value == '\x00'):
+                    print "Door locked successfully"
+                else:
+                    print "An error occurred in door locking"
+            elif(command == '\x01'):
+                print ''
+                print "( 0x0101 ) Door Lock: Unlock Door Response"
+                print time.strftime("%H:%M:%S", time.localtime())
+                value = rfdata[3]
+                if(value == '\x00'):
+                    print "Door unlocked successfully"
+                else:
+                    print "An error occurred in door unlocking"
+            return
+        elif framecontrol == '\x18':
+            if(command == '\x01'):
+                attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
+                if attributeId == 0x0000:
+                    value = rfdata[7]
+                    print "Door status: "
+                    if value == '\x00':
+                        print "Not fully locked"
+                    elif value == '\x01':
+                        print "Locked"
+                    elif value == '\x02':
+                        print "Unlocked"
+                    else:
+                        print "Unknown value: " + zigbeeHexStringToHexString(value)
+                           
+                    message = "type : zcl_read_attributes_response \n"
+                    message += "packet_id: " + packetId + "\n"
+                    message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
+                    message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
+                    message += "attributes: "
+
+                    attrIdStr = "%0.4x" % attributeId
+                    attrIdStr = changeEndian(attrIdStr)
+                    message += attrIdStr
+                    message += ", "
+
+                    zclPayload = parsedData['rf_data'][3:]
+                    zclPayload = zclPayload[3:]
+                    attributeType = zclPayload[0]
+                    message += "%0.2x" % ord(attributeType)
+                    message += ", "
+
+                    message += "success"
+                    message += ", "
+
+                    message += "%0.2x" % ord(value)
+                    message += ";"
+
+                    message += ";"
+                    
+                    message = message[0:len(message) - 1]
+                    message += "\n"
+
+                    # no one to send the response to so just move on
+                    if(tup == None):
+                        # cant really do anything here
+                        return
+                    sendSocket.sendto(message,tup[0])
+            elif command == '\x07':
+                status = rfdata[3]
+                print ''
+                print "( 0x0101 ) Door Lock: Configure reporting response"
+                print 'rfdata : ' + zigbeeHexStringToHexString(rfdata)
+                if status == '\x00':
+                    print "Configure report successfully"
+                    message = "type : zcl_configure_reporting_response \n"
+                    message += "packet_id: " + packetId + "\n"
+                    message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
+                    message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
+                    message += "attributes: " 
+                    message +=  "all_success \n";
+
+                    # no one to send the response to so just move on
+                    if(tup == None):
+                        # cant really do anything here
+                        return
+                    sendSocket.sendto(message,tup[0])
+                else:     
+                    print "Configure report unsuccessfully, status =", zigbeeHexStringToHexString(status)
+            elif(command == '\x0A'):
+                print "New update"
+                attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
+                if attributeId == 0x0000:
+                    value = rfdata[6]
+                    if value == '\x00':
+                        print "Not fully locked"
+                    elif value == '\x01':
+                        print "Locked"
+                    elif value == '\x02':
+                        print "Unlocked"
+                    else:
+                        print "Unknown value: " + zigbeeHexStringToHexString(value)
+
+                    message = "type : zcl_read_attributes_response \n"
+                    message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
+                    message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
+                    message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
+                    message += "attributes: "
+
+                    attrIdStr = "%0.4x" % attributeId
+                    attrIdStr = changeEndian(attrIdStr)
+                    message += attrIdStr
+                    message += ", "
+
+                    zclPayload = parsedData['rf_data'][3:]
+                    zclPayload = zclPayload[3:]
+                    attributeType = zclPayload[0]
+                    message += "%0.2x" % ord(attributeType)
+                    message += ", "
+
+                    message += "success"
+                    message += ", "
+
+                    message += "%0.2x" % ord(value)
+                    message += ";"
+
+                    message += ";"
+                    
+                    message = message[0:len(message) - 1]
+                    message += "\n"
+
+                    # get callback clients to respond to
+                    callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
+                    retAddr = None
+                    zigbeeHACallbackMutex.acquire()
+                    if(zibeeHACallback.has_key(callbackIndex)):
+                        retAddr = zibeeHACallback[callbackIndex]
+                    zigbeeHACallbackMutex.release()
+
+                    # no one to respond to so do nothing here
+                    if(retAddr == None):
+                        return
+                    for ra in retAddr:
+                        sendSocket.sendto(message,ra)
+            return
 
     # if this is a ZDO message/response
     if(parsedData['profile'] == '\x00\x00'):
 
+       # Added by Changwoo
+        # if this is a Match Descriptor Request so we need to answer.
+        if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
+            zigbeeConnectionMutex.acquire()
+            zigbeeConnection.send('tx_explicit',
+                            frame_id='\x08',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=parsedData['source_addr_long'],
+                            dest_addr=parsedData['source_addr'],
+                            src_endpoint='\x00',
+                            dest_endpoint='\x00',
+                            cluster='\x00\x06',
+                            profile='\x00\x00',
+                            data=parsedData['rf_data']
+                            )
+            time.sleep(1)
+            zigbeeConnection.send('tx_explicit',
+                            frame_id='\x40',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=parsedData['source_addr_long'],
+                            dest_addr=parsedData['source_addr'],
+                            src_endpoint='\x00',
+                            dest_endpoint='\x00',
+                            cluster='\x80\x06',
+                            profile='\x00\x00',
+                            data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
+                            )
+            time.sleep(1)
+            print ''
+            print '[ 0x0006 ] Match Descriptor Request - answered'
+            print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+            zigbeeConnectionMutex.release()
+
+
         # if this is a device announcement so we can get some useful data from it
-        if(parsedData['cluster'] == '\x00\x13'):
-            
+        elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
             # pick out the correct parts of the payload
             longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
             shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
@@ -865,20 +1573,41 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
                 zigbeeUnregisteredAddresses.remove(longAddr)
             zigbeeUnregisteredAddressesMutex.release()
 
+            # Added by Changwoo
+            zigbeeConnectionMutex.acquire()
+            zigbeeConnection.send('tx_explicit',
+                            frame_id='\x08',
+                            # frame_id=chr(seqNumber),
+                            dest_addr_long=parsedData['source_addr_long'],
+                            dest_addr=parsedData['source_addr'],
+                            src_endpoint='\x00',
+                            dest_endpoint='\x00',
+                            cluster='\x00\x13',
+                            profile='\x00\x00',
+                            data=parsedData['rf_data']
+                            )
+           print ''
+           print '[ 0x0013 ] device announcement - answered'
+           print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+           deviceAnnouncementSingleton = False
+            zigbeeConnectionMutex.release()
+
+
         # if this is a response to a zdo bind_req message
         elif(parsedData['cluster'] == '\x80\x21'):
 
             # get the status and sequence number from the message
             seqNumber = parsedData['rf_data'][0]
             statusCode = parsedData['rf_data'][1]
+            print "> response to a zdo bind_req message parsedData"
 
             # get the bind tuple information
             # for this specific bind request
             tup = None
-            zigeeBindRequestMutex.acquire() 
-            if(zigeeBindRequest.has_key(ord(seqNumber))):
-                tup = zigeeBindRequest[ord(seqNumber)]
-            zigeeBindRequestMutex.release()
+            zigbeeBindRequestMutex.acquire() 
+            if(zigbeeBindRequest.has_key(ord(seqNumber))):
+                tup = zigbeeBindRequest[ord(seqNumber)]
+            zigbeeBindRequestMutex.release()
 
             if(tup == None):
                 # cant really do anything in this case...
@@ -890,16 +1619,18 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
                 # add a callback for this specific device and cluster 
                 # to the HA callback dict 
-                zibeeHACallbackMutex.acquire();
+                zigbeeHACallbackMutex.acquire();
                 if(zibeeHACallback.has_key((tup[0], tup[1]))):
                     if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
                         zibeeHACallback[(tup[0], tup[1])].append(tup[3])
                 else:
                     zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
-                zibeeHACallbackMutex.release()
-
+                zigbeeHACallbackMutex.release()
+                
                 # send success message
                 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
+                
+                print "> Success message sent!"
 
             # Not Supported
             elif (ord(statusCode) == 170):
@@ -915,6 +1646,7 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
         # if this is a response to a short address query
         elif(parsedData['cluster'] == '\x80\x00'):
+            #print ">response to a short address query 0x8000"
             
             # get a status code
             statusCode = parsedData['rf_data'][0]
@@ -939,6 +1671,27 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
             zigbeeLongShortAddr[longAddr] = shortAddr
             zigbeeLongShortAddrMutex.release()
 
+       # Added by Changwoo
+        elif(parsedData['cluster'] == '\x80\x06'):
+           print ''
+           print '[ 0x8006 ] get Match Descriptor Response'
+           print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+
+       # Added by Changwoo
+        elif(parsedData['cluster'] == '\x80\x36'):
+           print ''
+           print '[ 0x8036 ] get Management Permit Joining Response'
+           print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+
+           ManagementPermitJoiningReqSuccess = True
+
+       # Added by Changwoo
+        else :
+           print ''
+           print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
+           print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+
+
     # if this is a home automation zcl message/response
     elif (parsedData['profile'] == '\x01\x04'):
 
@@ -946,9 +1699,45 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
         zclFrameControl = parsedData['rf_data'][0]
         zclSeqNumber = parsedData['rf_data'][1]
         zclCommand = parsedData['rf_data'][2]
+       zclStatus = parsedData['rf_data'][3]
+
+       # Added by Changwoo
+        if(zclCommand == '\x00'):
+           print ''
+           print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
+           
+           # find who to send response 
+           tup = None
+            zigbeeSeqNumberToClientMutex.acquire()
+
+           if(longAddr in seqNumberForNotification):
+               key = longAddr
+                if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
+                    tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
+                    #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
+            zigbeeSeqNumberToClientMutex.release()
+
+            # no one to send the response to so just move on
+            if(tup == None):
+                # cant really do anything here
+                return
+            # create the response message
+            packetId = tup[2]
+            message = "type : zcl_zone_status_change_notification\n"
+            message += "packet_id: " + packetId + "\n"
+            message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
+            message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
+            message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
+            message += "attributes: success"
+            message += "\n"
+            # send the socket
+            sendSocket.sendto(message,tup[0])
+           print(">port : ", tup[0][1])
+
+
 
         # this is a zcl read attribute response
-        if(zclCommand == '\x01'):
+        elif(zclCommand == '\x01'):
 
             # get the zcl payload
             zclPayload = parsedData['rf_data'][3:]
@@ -1037,9 +1826,51 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
             message = message[0:len(message) - 1]
             message += "\n"
-
             # send the socket
-            sendSoceket.sendto(message,tup[0])
+            sendSocket.sendto(message,tup[0])
+
+       # Added by Changwoo
+    # this is a zcl write attribute response
+       elif(zclCommand == '\x04'):
+
+            # get the zcl payload
+            zclPayload = parsedData['rf_data'][3]
+           # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
+           if(zclStatus == '\x70' or zclPayload == '\x00'):
+
+                # find who to send response to 
+                tup = None
+                zigbeeSeqNumberToClientMutex.acquire()
+                if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
+                    tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
+                   seqNumberForNotification[longAddr] = ord(zclSeqNumber)
+                    #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
+                zigbeeSeqNumberToClientMutex.release()
+                # no one to send the response to so just move on
+                if(tup == None):
+                    # cant really do anything here
+                    return
+            
+                # create the response message
+                packetId = tup[2]
+                message = "type : zcl_write_attributes_response\n"
+                message += "packet_id: " + packetId + "\n"
+                message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
+                message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
+                message += "attributes: success"
+                message += "\n"
+                # send the socket
+                sendSocket.sendto(message,tup[0])
+               print ''
+               print '[ 0x0500 ] get Write Attribute Response success'
+               print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+
+           else:
+               print ''
+               print '[ 0x0500 ] get Write Attribute Response'
+               print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
+
+
 
         # this is a zcl configure attribute response
         elif(zclCommand == '\x07'):
@@ -1072,7 +1903,7 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
                 # if all the configurations are a success then only send back a success
                 # based on zigbee specs
                 message +=  "all_success \n";
-                sendSoceket.sendto(message,tup[0])
+                sendSocket.sendto(message,tup[0])
             
             else:
                 attibuteResponseList = []
@@ -1110,11 +1941,11 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
                 message = message[0:len(message) - 1]
                 message += "\n"
-                sendSoceket.sendto(message,tup[0])
+                sendSocket.sendto(message,tup[0])
 
         # this is a zcl report attribute message
         elif(zclCommand == '\x0a'):
-
+           print "get Report attribute "
             # get teh zcl payload
             zclPayload = parsedData['rf_data'][3:]
             attibuteResponseList = []
@@ -1141,10 +1972,10 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
             # get callback clients to respond to
             callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
             retAddr = None
-            zibeeHACallbackMutex.acquire()
+            zigbeeHACallbackMutex.acquire()
             if(zibeeHACallback.has_key(callbackIndex)):
                 retAddr = zibeeHACallback[callbackIndex]
-            zibeeHACallbackMutex.release()
+            zigbeeHACallbackMutex.release()
 
             # no one to respond to so do nothing here
             if(retAddr == None):
@@ -1177,10 +2008,11 @@ def processZigbeeRxExplicitCommandMessage(parsedData):
 
             message = message[0:len(message) - 1]
             message += "\n"
-
+           print "Sending", message
+           
             # send to all client that want this callback
             for ra in retAddr:
-                sendSoceket.sendto(message,ra)
+                sendSocket.sendto(message,ra)
 
 # -----------------------------------------------------------------------------
 # Communication Callback/Parse Methods
@@ -1190,22 +2022,26 @@ def handleNewZigbeeMessage(parsedData):
 
         parsedData -- Pre-parsed (into a dict) data from message.
     '''
-    print "=================================================================="
-    print "New Zigbee Message"
-    # printMessageData(parsedData)
+    #print "=================================================================="
+    #print ''
+    #print "New Zigbee Message"
+    #printMessageData(parsedData)
 
     # dispatch to the correct zigbee handler
     if (parsedData['id'] == 'at_response'):
+        #print "parsedDataID : at_response"
         processZigbeeATCommandMessage(parsedData)
 
     elif (parsedData['id'] == 'rx_explicit'):
-        #printMessageData(parsedData)
+        #print "parsedDataID : rx_explicit"
         processZigbeeRxExplicitCommandMessage(parsedData)
 
-    else:
-        print "Unknown API format"
+    #else:
+        #print "Unknown API format"
+
+    #print "=================================================================="
+
 
-    print "=================================================================="
 
 def handleNewUdpPacket(data, addr):
     ''' Method to parse and handle an incoming UDP packet.
@@ -1213,10 +2049,12 @@ def handleNewUdpPacket(data, addr):
         data -- Data that was in the UDP packet.
         addr -- Address (IP and Port) of the UDP packet origin.
     '''
+    global ManagementPermitJoiningReqSuccess
 
-    print "=================================================================="
-    print "Got New UDP packet..."
-    # print data
+    #print "=================================================================="
+    #print ''
+    #print "Got New UDP packet..."
+    #print data
 
 
     # data comes in as 'key: value\n key: value...' string and so needs to be
@@ -1236,24 +2074,42 @@ def handleNewUdpPacket(data, addr):
             # from improper packing on the sender side
             parsedData[fields[0].strip()] = fields[1].strip()
 
+
     # wrap in try statement just in case there is an improperly formated packet we
     # can deal with it
     try:
         # dispatch to the correct process method
         if(parsedData["type"] == "zdo_bind_request"):
+            print "> processUdpZdoBindReqMessage call"
             processUdpZdoBindReqMessage(parsedData, addr)
         elif(parsedData["type"] == "zdo_unbind_request"):
             processUdpZdoUnBindReqMessage(parsedData, addr)
         elif(parsedData["type"] == "send_address"):
+            print "> processUdpSendAddressMessage call"
             processUdpSendAddressMessage(parsedData, addr)
         elif(parsedData["type"] == "zcl_read_attributes"):
             processUdpZclReadAttributesMessage(parsedData, addr)
         elif(parsedData["type"] == "zcl_configure_reporting"):
+            print "> processUdpZclConfigureReportingMessage call"
             processUdpZclConfigureReportingMessage(parsedData, addr)
         elif(parsedData["type"] == "policy_set"):
             processUdpPolicySet(parsedData, addr)
         elif(parsedData["type"] == "policy_clear"):
             processUdpPolicyClear(parsedData, addr)
+        elif(parsedData["type"] == "management_permit_joining_request"): # Added by Changwoo
+            processUdpManagementPermitJoiningReqMessage(parsedData, addr)
+        elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): # Added by Changwoo
+            processUdpZclWriteAttributesMessage(parsedData, addr)
+        elif(parsedData["type"] == "zcl_enrollment_response"): # Added by Changwoo
+            processUdpEnrollmentResponse(parsedData, addr)
+        elif(parsedData["type"] == "zdo_broadcast_route_record_request"): # Added by Changwoo
+            processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
+        elif(parsedData["type"] == "zcl_change_switch_request"): # Added by Changwoo
+            processUdpZclChangeSwitchReqMessage(parsedData, addr)
+        elif(parsedData["type"] == "zcl_lock_or_unlock_door_request"): # Added by Jiawei
+            processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr)
+        elif(parsedData["type"] == "zcl_read_door_status_request"): # Added by Jiawei
+            processUdpZclReadDoorStatusReqMessage(parsedData, addr)
         else:
             #print "unknown Packet: " + parsedData["type"]
             pass
@@ -1263,7 +2119,7 @@ def handleNewUdpPacket(data, addr):
         print "I didn't expect this error:", sys.exc_info()[0]
         traceback.print_exc()
 
-    print "=================================================================="
+    #print "=================================================================="
 
 
 # -----------------------------------------------------------------------------
@@ -1297,7 +2153,7 @@ def main():
 
     # setup incoming UDP socket and bind it to self and specified UDP port
     # sending socket does not need to be bound to anything
-    receiveSoceket.bind(('127.0.0.1', UDP_RECEIVE_PORT))
+    receiveSocket.bind((LOCAL_ADDRESS, UDP_RECEIVE_PORT))
 
     # create the thread that does short address lookups
     addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
@@ -1307,12 +2163,13 @@ def main():
         # Main running loop
         while(True):
             print "=================================================================="
+            print ''
             print "Waiting..."
             print "=================================================================="
 
             # wait for an incoming UDP packet
             # this is a blocking call
-            data, addr = receiveSoceket.recvfrom(4096)
+            data, addr = receiveSocket.recvfrom(4096)
 
             # handle the UDP packet appropriately
             handleNewUdpPacket(data, addr)
@@ -1334,8 +2191,8 @@ def main():
 
     # make sure to close all the connections
     zigbeeConnection.halt()
-    receiveSoceket.close()
-    sendSoceket.close()
+    receiveSocket.close()
+    sendSocket.close()
 
 if __name__ == "__main__":
     # call main function since this is being run as the start