1 # -----------------------------------------------------------------------------
2 # ZigBee Gateway Driver for Vigilia v.1.0
3 # Created by Ali Younis
4 # Modified by Rahmadi Trimananda, Lee Changwoo, Jiawei
5 # (c) 2016-2018 University of California, Irvine
6 # -----------------------------------------------------------------------------
8 from xbee import ZigBee
16 from threading import Thread, Lock
21 # -----------------------------------------------------------------------------
22 # Constants ans Pseudo-Constants
23 # -----------------------------------------------------------------------------
24 UDP_RECEIVE_PORT = 5005 # port used for incoming UDP data
25 UDP_RECEIVE_BUFFER_SIZE = 4096 # max buffer size of an incoming UDP packet
26 SYSTEM_MASTER_ADDRESS = ("192.168.1.198", 12345) # ip address and portof the system master node
27 LOCAL_ADDRESS = "192.168.1.192" # local IP address
29 # time for messages to wait for a response before the system clears away that
31 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5
33 #ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH" # USB-Serial port of local radio
34 ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
35 ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port
37 # address of our local zigbee radio
38 ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx"
40 # -----------------------------------------------------------------------------
41 # Global Variables and Objects
42 # -----------------------------------------------------------------------------
44 # signals to see if a request needs to be made
45 didGetLocalRadioHighAddress = False;
46 didGetLocalRadioLowAddress = False;
48 # zigbee communications object and its mutex
49 zigbeeConnection = None
50 zigbeeConnectionMutex = Lock()
52 #singleton mabe by Changwoo
53 matchDescriptorReqSingleton = True
54 deviceAnnouncementSingleton = True
55 ManagementPermitJoiningReqSuccess = False
57 # zigbee mapping from long to short object dict
58 zigbeeLongShortAddr = dict()
59 zigbeeLongShortAddrMutex = Lock()
61 # zigbee mapping from a sequence number to a client
62 # for correct response handling
63 zigbeeSeqNumberToClient = dict()
64 zigbeeSeqNumberToClientMutex = Lock()
66 zigbeeBindRequest = dict()
67 zigbeeBindRequestMutex = Lock()
69 # Keeps record of where to send callbacks to when an HA message is received
70 zibeeHACallback = dict()
71 zigbeeHACallbackMutex = Lock()
74 # Keeps a record of device addresses whose short addresses have not been
76 zigbeeUnregisteredAddresses = []
77 zigbeeUnregisteredAddressesMutex = Lock()
79 # used to signal all threads to end
83 # 2 sockets, one for sending (not bound to a port manually)
84 # and one for receiving, known port binding by application
86 sendSocket = socket(AF_INET, SOCK_DGRAM)
87 receiveSocket = socket(AF_INET, SOCK_DGRAM)
90 # zigbee address authority list
91 zigbeeAddressAuthorityDict = dict()
94 seqNumberForNotification = dict()
96 # -----------------------------------------------------------------------------
98 # -----------------------------------------------------------------------------
99 def reverseShortAddress(shortAddr):
100 result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
103 def parseCommandLineArgs(argv):
104 global ZIGBEE_SERIAL_PORT
105 global ZIGBEE_SERIAL_BAUD
107 opts, args = getopt.getopt(
108 argv, "hp:b:u:", ["port=", "baud=", "udpport="])
110 except getopt.GetoptError:
111 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
114 for opt, arg in opts:
116 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
119 elif opt in ("-p", "--port"):
120 ZIGBEE_SERIAL_PORT = arg
122 elif opt in ("-b", "--baud"):
124 ZIGBEE_SERIAL_BAUD = int(arg)
126 print "Buad rate must be an integer"
131 # Convenience (Stateless)
134 def hexListToChar(hexList):
135 ''' Method to convert a list/string of characters into their corresponding values
137 hexList -- list or string of hex characters
141 retString += chr(int(h, 16))
144 def splitByN(seq, n):
145 ''' Method to split a string into groups of n characters
150 return [seq[i:i+n] for i in range(0, len(seq), n)]
152 def changeEndian(hexString):
153 ''' Method to change endian of a hex string
155 hexList -- string of hex characters
157 split = splitByN(hexString, 2) # get each byte
158 split.reverse(); # reverse ordering of the bytes
166 def printMessageData(data):
167 ''' Method to print a zigbee message to the console
169 data -- pre-parsed zigbee message
174 print "{0:02x}".format(ord(e)),
176 print "({})".format(data[d]),
179 def hexStringToZigbeeHexString(hexString):
180 ''' Method to change a hex string to a string of characters with the hex values
182 hexList -- string of hex characters
184 return hexListToChar(splitByN(hexString, 2))
186 def zigbeeHexStringToHexString(zigbeeHexString):
187 ''' Method to change string of characters with the hex values to a hex string
189 hexList -- string of characters with hex values
193 for e in zigbeeHexString:
194 retString += "{0:02x}".format(ord(e))
197 def zclDataTypeToBytes(zclPayload):
198 ''' Method to determine data length of a zcl attribute
200 zclPayload -- ZCL payload data, must have dataType as first byte
202 attrType = ord(zclPayload[0])
204 if(attrType == 0x00):
206 elif (attrType == 0x08):
208 elif (attrType == 0x09):
210 elif (attrType == 0x0a):
212 elif (attrType == 0x0b):
214 elif (attrType == 0x0c):
216 elif (attrType == 0x0d):
218 elif (attrType == 0x0e):
220 elif (attrType == 0x0f):
222 elif (attrType == 0x10):
224 elif (attrType == 0x18):
226 elif (attrType == 0x19):
228 elif (attrType == 0x1a):
230 elif (attrType == 0x1b):
232 elif (attrType == 0x1c):
234 elif (attrType == 0x1d):
236 elif (attrType == 0x1e):
238 elif (attrType == 0x1f):
240 elif (attrType == 0x20):
242 elif (attrType == 0x21):
244 elif (attrType == 0x22):
246 elif (attrType == 0x23):
248 elif (attrType == 0x24):
250 elif (attrType == 0x25):
252 elif (attrType == 0x26):
254 elif (attrType == 0x27):
256 elif (attrType == 0x28):
258 elif (attrType == 0x29):
260 elif (attrType == 0x2a):
262 elif (attrType == 0x2b):
264 elif (attrType == 0x2c):
266 elif (attrType == 0x2d):
268 elif (attrType == 0x2e):
270 elif (attrType == 0x2f):
272 elif (attrType == 0x30):
274 elif (attrType == 0x31):
276 elif (attrType == 0x38):
278 elif (attrType == 0x39):
280 elif (attrType == 0x3a):
282 elif (attrType == 0x41):
283 return ord(zclPayload[1])
284 elif (attrType == 0x42):
285 return ord(zclPayload[1])
286 elif (attrType == 0x43):
287 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
288 elif (attrType == 0x44):
289 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
290 elif (attrType == 0xe0):
292 elif (attrType == 0xe1):
294 elif (attrType == 0xe2):
296 elif (attrType == 0xe8):
298 elif (attrType == 0xe9):
300 elif (attrType == 0xea):
302 elif (attrType == 0xf0):
304 elif (attrType == 0xf1):
306 elif (attrType == 0xff):
313 def createSequenceNumberForClient(addr, packetId):
314 ''' Method to get and store a sequence number with a specific client
315 for a specific message.
317 addr -- UDP address of the client to associate with the seq. number
318 packetId -- packet id from the UDP packet
320 # keep trying to find a number to return
323 # get the current time
324 epoch_time = int(time.time())
326 # get the current list of used numbers
327 zigbeeSeqNumberToClientMutex.acquire()
328 keysList = zigbeeSeqNumberToClient.keys()
329 zigbeeSeqNumberToClientMutex.release()
331 # if all the numbers are already used
332 if(len(keysList) == 256):
334 # get a list of all the items
335 zigbeeSeqNumberToClientMutex.acquire()
336 itemsList = zigbeeSeqNumberToClient.items()
337 zigbeeSeqNumberToClientMutex.release()
339 # search for a number that is old enough to get rid of otherwise use -1
341 for item in itemsList:
342 if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC):
347 # replace the record with new data if we found one to replace
348 zigbeeSeqNumberToClientMutex.acquire()
349 zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId)
350 zigbeeSeqNumberToClientMutex.release()
355 # not all numbers used yet so pick one randomly
356 randNum = random.randrange(0,256)
358 # check if we are using the number yet
359 if(randNum not in keysList):
361 # we are not so insert to keep track who this number is for and return it
362 zigbeeSeqNumberToClientMutex.acquire()
363 zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId)
364 zigbeeSeqNumberToClientMutex.release()
367 def getConnectedRadioLongAddress():
368 """ Method to make sure we get the MAC address of our local radio"""
369 global zigbeeConnection
372 # keep looping until we get both the MSBs and the LSBs
373 while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)):
376 zigbeeConnection.send('at', command="SH")
377 zigbeeConnection.send('at', command="SL")
379 # sleep for a bit to give the radio time to respond before we check again
382 def addressUpdateWorkerMethod():
383 ''' Method to keep refreshing the short addresses of the known zigbee devices'''
385 global zigbeeLongShortAddr
386 global zigbeeLongShortAddrMutex
387 global zigbeeUnregisteredAddresses
388 global zigbeeUnregisteredAddressesMutex
389 global zigbeeConnectionMutex
390 global zigbeeConnection
392 # keep looping until signaled to quit
393 while(not doEndFlag):
397 # add unregistered (short addresses unknown) devices so
398 # that we can look them up
399 zigbeeUnregisteredAddressesMutex.acquire()
400 addrList.extend(zigbeeUnregisteredAddresses)
401 zigbeeUnregisteredAddressesMutex.release()
403 # add the devices that we have short addresses for so we can
404 # get their most recent short addresses
405 zigbeeLongShortAddrMutex.acquire()
406 addrList.extend(zigbeeLongShortAddr.keys())
407 zigbeeLongShortAddrMutex.release()
409 # Loop through all the addresses and send messages for each address
412 # create payload for a query on the network for a short address
414 payload += hexStringToZigbeeHexString(changeEndian(ad))
417 # create and send binding command
418 zigbeeConnectionMutex.acquire()
420 zigbeeConnection.send('tx_explicit',
422 dest_addr_long=hexStringToZigbeeHexString(ad),
423 dest_addr='\xff\xfd',
425 dest_endpoint='\x00',
430 zigbeeConnectionMutex.release()
439 def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
440 ''' Method to send a success or fail back to a client.
442 addr -- UDP address to send packet to
443 packetTypeStr -- name of this specific packet
444 packetIdStr -- packet id to send
445 sucOrFail -- whether this is a success or fail message (True = success)
446 reason -- reason of failure (if needed, default is None)
452 # construct the message
453 message = "type: " + packetTypeStr.strip() + "\n"
454 message += "packet_id: " + packetIdStr + "\n"
457 message += "response: success \n"
459 message += "response: fail \n"
460 message += "reason: " + reason + "\n"
462 # send message in a UDP packet
463 sendSocket.sendto(message,addr)
465 def processUdpZdoBindReqMessage(parsedData, addr):
469 if(zigbeeAddressAuthorityDict.has_key(addr)):
470 l = zigbeeAddressAuthorityDict[addr]
471 if(parsedData['device_address_long'] not in l):
476 # get the short address for this device long address if possible
477 zigbeeLongShortAddrMutex.acquire()
478 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
479 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
480 zigbeeLongShortAddrMutex.release()
482 # if there is a short address than we can send the message
483 # if there is not one then we cannot since we need both the short and
485 if(shortAddr != None):
486 print "> Short address exists", shortAddr
488 # get a request number
489 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
494 # send an error message, could not get a sequence number to use at this time
495 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
498 # a bind request was made so must store and wait for response
499 # before we setup callbacks, so keep just the data we need to create the callback
500 zigbeeBindRequestMutex.acquire()
501 zigbeeBindRequest[seqNumber] = (parsedData['device_address_long'],
502 parsedData['cluster_id'],
503 parsedData['packet_id'],
505 zigbeeBindRequestMutex.release()
507 # construct the short and long addresses of the message for sending
508 # make sure they are in the correct format
509 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
510 destShortAddr = hexStringToZigbeeHexString(shortAddr)
512 # create the payload data
514 payloadData += chr(seqNumber)
515 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
516 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
517 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id']))
518 payloadData += '\x03'
519 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
520 payloadData += '\x00'
522 # create and send binding command
523 zigbeeConnectionMutex.acquire()
524 zigbeeConnection.send('tx_explicit',
526 # frame_id=chr(seqNumber),
527 dest_addr_long=destLongAddr,
528 dest_addr=destShortAddr,
530 dest_endpoint='\x00',
535 zigbeeConnectionMutex.release()
539 # send a failure packet since there is no short address available
540 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
543 def processUdpZdoUnBindReqMessage(parsedData, addr):
544 zigbeeHACallbackMutex.acquire();
545 if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
546 zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
547 zigbeeHACallbackMutex.release()
548 sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
552 def processUdpSendAddressMessage(parsedData, addr):
553 ''' Method handle a send address command
555 parsedData -- Pre-parsed Data that was in the UDP packet.
556 addr -- Address (IP and Port) of the UDP packet origin.
558 global zigbeeLongShortAddr
559 global zigbeeLongShortAddrMutex
560 global zigbeeUnregisteredAddresses
561 global zigbeeUnregisteredAddressesMutex
564 print "process send address"
567 # construct success message
568 message = "type: send_address_response\n"
569 message += "packet_id: " + parsedData['packet_id'] + "\n"
570 message += "response: success\n"
572 # tell client that we got their request
573 sendSocket.sendto(message,addr)
574 print "responding", message
577 zigbeeLongShortAddrMutex.acquire()
578 doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
579 zigbeeLongShortAddrMutex.release()
582 # long address is already registered with the system so no need to do anything
585 # long address not registered so add it for short address lookup
586 zigbeeUnregisteredAddressesMutex.acquire()
587 zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
588 zigbeeUnregisteredAddressesMutex.release()
593 def processUdpEnrollmentResponse(parsedData, addr):
595 global zigbeeLongShortAddr
596 global zigbeeLongShortAddrMutex
597 global zigbeeBindRequestMutex
598 global zigbeeBindRequest
599 global zigbeeConnectionMutex
600 global zigbeeConnection
603 # get the short address for this device long address if possible
604 zigbeeLongShortAddrMutex.acquire()
605 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
606 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
607 zigbeeLongShortAddrMutex.release()
610 # if there is a short address than we can send the message
611 # if there is not one then we cannot since we need both the short and
613 if(shortAddr != None):
615 # get a request number
616 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
621 # send an error message, could not get a sequence number to use at this time
622 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
625 # get the info for sending
626 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
627 destShortAddr = hexStringToZigbeeHexString(shortAddr)
628 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
629 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
630 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
632 # create the payload data
634 payloadData += '\x01'
635 payloadData += chr(seqNumber)
636 payloadData += '\x00'
637 payloadData += '\x00\x00'
639 # create and send binding command
640 zigbeeConnectionMutex.acquire()
641 zigbeeConnection.send('tx_explicit',
643 # frame_id=chr(seqNumber),
644 dest_addr_long=destLongAddr,
645 dest_addr=destShortAddr,
647 dest_endpoint=dstEndpoint,
652 print '> EnrollmentResponse is sent'
653 zigbeeConnectionMutex.release()
657 # send a fail response
658 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
665 def processUdpZclWriteAttributesMessage(parsedData, addr):
667 global zigbeeLongShortAddr
668 global zigbeeLongShortAddrMutex
669 global zigbeeBindRequestMutex
670 global zigbeeBindRequest
671 global zigbeeConnectionMutex
672 global zigbeeConnection
675 # get the short address for this device long address if possible
676 zigbeeLongShortAddrMutex.acquire()
677 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
678 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
679 zigbeeLongShortAddrMutex.release()
681 # if there is a short address than we can send the message
682 # if there is not one then we cannot since we need both the short and
684 if(shortAddr != None):
685 # get a request number
686 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
691 # send an error message, could not get a sequence number to use at this time
692 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
695 # get the info for sending
696 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
697 destShortAddr = hexStringToZigbeeHexString(shortAddr)
698 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
699 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
700 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
702 # create the payload data
704 payloadData += '\x00'
705 payloadData += chr(seqNumber)
706 payloadData += '\x02'
707 payloadData += '\x10\x00'
708 payloadData += '\xF0'
709 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
711 zigbeeConnectionMutex.acquire()
712 zigbeeConnection.send('tx_explicit',
714 # frame_id=chr(seqNumber),
715 dest_addr_long=destLongAddr,
716 dest_addr=destShortAddr,
718 dest_endpoint=dstEndpoint,
725 print '> WriteAttributesReq is sent : '+str(shortAddr)
726 zigbeeConnectionMutex.release()
730 # send a fail response
731 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
735 def processUdpZclChangeSwitchReqMessage(parsedData, addr):
737 global zigbeeLongShortAddr
738 global zigbeeLongShortAddrMutex
739 global zigbeeBindRequestMutex
740 global zigbeeBindRequest
741 global zigbeeConnectionMutex
742 global zigbeeConnection
745 # get the short address for this device long address if possible
746 zigbeeLongShortAddrMutex.acquire()
747 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
748 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
749 zigbeeLongShortAddrMutex.release()
752 # if there is a short address than we can send the message
753 # if there is not one then we cannot since we need both the short and
755 if(shortAddr != None):
757 # get a request number
758 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
763 # send an error message, could not get a sequence number to use at this time
764 sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
767 # get the info for sending
768 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
769 destShortAddr = hexStringToZigbeeHexString(shortAddr)
770 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
771 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
772 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
773 value = hexStringToZigbeeHexString(parsedData['value'])
775 # create and send binding command
776 zigbeeConnectionMutex.acquire()
778 zigbeeConnection.send('tx_explicit',
780 # frame_id=chr(seqNumber),
781 dest_addr_long=destLongAddr,
782 dest_addr=destShortAddr,
784 dest_endpoint=dstEndpoint,
787 data='\x01'+chr(seqNumber)+value
790 if parsedData['value']==1:
791 print '> The outlet sensor turned on'
793 print '> The outlet sensor turned off'
795 zigbeeConnectionMutex.release()
799 # send a fail response
800 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
805 def processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr):
807 global zigbeeLongShortAddr
808 global zigbeeLongShortAddrMutex
809 global zigbeeBindRequestMutex
810 global zigbeeBindRequest
811 global zigbeeConnectionMutex
812 global zigbeeConnection
815 # get the short address for this device long address if possible
816 zigbeeLongShortAddrMutex.acquire()
817 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
818 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
819 zigbeeLongShortAddrMutex.release()
822 # if there is a short address than we can send the message
823 # if there is not one then we cannot since we need both the short and
825 if(shortAddr != None):
827 # get a request number
828 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
833 # send an error message, could not get a sequence number to use at this time
834 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'out_of_space')
837 # get the info for sending
838 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
839 destShortAddr = hexStringToZigbeeHexString(shortAddr)
840 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
841 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
842 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
843 value = hexStringToZigbeeHexString(parsedData['value'])
845 # create and send binding command
846 zigbeeConnectionMutex.acquire()
848 zigbeeConnection.send('tx_explicit',
850 dest_addr_long=destLongAddr,
851 dest_addr=destShortAddr,
853 dest_endpoint=dstEndpoint,
856 data='\x01'+chr(seqNumber)+value
860 print '> The door lock is unlocked'
861 elif value == '\x00':
862 print '> The door lock is locked'
864 print '> Unknown door lock value: ' + str(value)
866 zigbeeConnectionMutex.release()
870 # send a fail response
871 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'short_address_unknown')
875 def processUdpZclReadDoorStatusReqMessage(parsedData, addr):
877 global zigbeeLongShortAddr
878 global zigbeeLongShortAddrMutex
879 global zigbeeBindRequestMutex
880 global zigbeeBindRequest
881 global zigbeeConnectionMutex
882 global zigbeeConnection
885 # get the short address for this device long address if possible
886 zigbeeLongShortAddrMutex.acquire()
887 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
888 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
889 zigbeeLongShortAddrMutex.release()
892 # if there is a short address than we can send the message
893 # if there is not one then we cannot since we need both the short and
895 if(shortAddr != None):
897 # get a request number
898 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
903 # send an error message, could not get a sequence number to use at this time
904 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'out_of_space')
907 # get the info for sending
908 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
909 destShortAddr = hexStringToZigbeeHexString(shortAddr)
910 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
911 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
912 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
914 # create and send binding command
915 zigbeeConnectionMutex.acquire()
917 zigbeeConnection.send('tx_explicit',
919 dest_addr_long=destLongAddr,
920 dest_addr=destShortAddr,
922 dest_endpoint=dstEndpoint,
925 data='\x10' + chr(seqNumber) + '\x00' + '\x00\x00'
929 zigbeeConnectionMutex.release()
930 print "send read door status"
933 # send a fail response
934 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'short_address_unknown')
938 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
940 global zigbeeLongShortAddr
941 global zigbeeLongShortAddrMutex
942 global zigbeeBindRequestMutex
943 global zigbeeBindRequest
944 global zigbeeConnectionMutex
945 global zigbeeConnection
948 # get the short address for this device long address if possible
949 zigbeeLongShortAddrMutex.acquire()
950 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
951 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
952 zigbeeLongShortAddrMutex.release()
955 # if there is a short address than we can send the message
956 # if there is not one then we cannot since we need both the short and
958 if(shortAddr != None):
960 # get a request number
961 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
966 # send an error message, could not get a sequence number to use at this time
967 sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
970 # get the info for sending
971 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
972 destShortAddr = hexStringToZigbeeHexString(shortAddr)
973 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
975 # create and send binding command
976 zigbeeConnectionMutex.acquire()
978 zigbeeConnection.send('tx_explicit',
980 # frame_id=chr(seqNumber),
981 dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
982 dest_addr='\xff\xfe',
984 dest_endpoint=dstEndpoint,
990 print '> BroadcastingRouteRecordReq is sent'
992 zigbeeConnectionMutex.release()
996 # send a fail response
997 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1002 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
1004 global zigbeeLongShortAddr
1005 global zigbeeLongShortAddrMutex
1006 global zigbeeBindRequestMutex
1007 global zigbeeBindRequest
1008 global zigbeeConnectionMutex
1009 global zigbeeConnection
1010 global matchDescriptorReqSingleton
1013 # get the short address for this device long address if possible
1014 zigbeeLongShortAddrMutex.acquire()
1015 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1016 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1017 zigbeeLongShortAddrMutex.release()
1020 # if there is a short address than we can send the message
1021 # if there is not one then we cannot since we need both the short and
1023 if(shortAddr != None):
1025 # get a request number
1026 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1029 if(seqNumber == -1):
1031 # send an error message, could not get a sequence number to use at this time
1032 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
1035 # get the info for sending
1036 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1037 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1038 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1040 # create the payload data
1042 payloadData += chr(seqNumber)
1043 payloadData += '\x5a'
1044 payloadData += '\x00'
1046 # create and send binding command
1047 zigbeeConnectionMutex.acquire()
1048 zigbeeConnection.send('tx_explicit',
1050 # frame_id=chr(seqNumber),
1051 dest_addr_long=destLongAddr,
1052 dest_addr=destShortAddr,
1053 src_endpoint='\x00',
1054 dest_endpoint='\x00',
1059 print '> ManagementPermitJoiningReq is sent'
1062 matchDescriptorReqSingleton= False
1063 zigbeeConnectionMutex.release()
1067 # send a fail response
1068 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
1072 def processUdpZclReadAttributesMessage(parsedData, addr):
1073 ''' Method handle a ZCL read attribute command
1075 parsedData -- Pre-parsed Data that was in the UDP packet.
1076 addr -- Address (IP and Port) of the UDP packet origin.
1079 global zigbeeLongShortAddr
1080 global zigbeeLongShortAddrMutex
1081 global zigbeeBindRequestMutex
1082 global zigbeeBindRequest
1083 global zigbeeConnectionMutex
1084 global zigbeeConnection
1088 if(zigbeeAddressAuthorityDict.has_key(addr)):
1089 l = zigbeeAddressAuthorityDict[addr]
1090 if(parsedData['device_address_long'] not in l):
1098 # get the short address for this device long address if possible
1099 zigbeeLongShortAddrMutex.acquire()
1100 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1101 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1102 zigbeeLongShortAddrMutex.release()
1105 # if there is a short address than we can send the message
1106 # if there is not one then we cannot since we need both the short and
1108 if(shortAddr != None):
1110 # get a request number
1111 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1114 if(seqNumber == -1):
1116 # send an error message, could not get a sequence number to use at this time
1117 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
1120 # get the info for sending
1121 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1122 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1123 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1124 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1125 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1127 # get all the attributes
1128 attributeIds = parsedData['attribute_ids'].split(',')
1130 # create the payload data
1132 payloadData += '\x00'
1133 payloadData += chr(seqNumber)
1134 payloadData += '\x00'
1136 # make all the attributes payloads
1137 for attr in attributeIds:
1139 attr = changeEndian(attr)
1140 payloadData += hexStringToZigbeeHexString(attr)
1142 # create and send binding command
1143 zigbeeConnectionMutex.acquire()
1144 zigbeeConnection.send('tx_explicit',
1146 # frame_id=chr(seqNumber),
1147 dest_addr_long=destLongAddr,
1148 dest_addr=destShortAddr,
1149 src_endpoint='\x00',
1150 dest_endpoint=dstEndpoint,
1155 zigbeeConnectionMutex.release()
1159 # send a fail response
1160 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1163 def processUdpZclConfigureReportingMessage(parsedData, addr):
1164 ''' Method handle a zcl configure reporting message
1166 parsedData -- Pre-parsed Data that was in the UDP packet.
1167 addr -- Address (IP and Port) of the UDP packet origin.
1170 global zigbeeLongShortAddr
1171 global zigbeeLongShortAddrMutex
1172 global zigbeeBindRequestMutex
1173 global zigbeeBindRequest
1174 global zigbeeConnectionMutex
1175 global zigbeeConnection
1177 if(zigbeeAddressAuthorityDict.has_key(addr)):
1178 l = zigbeeAddressAuthorityDict[addr]
1179 if(parsedData['device_address_long'] not in l):
1187 # get the short address for this device long address if possible
1188 zigbeeLongShortAddrMutex.acquire()
1189 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1190 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1191 zigbeeLongShortAddrMutex.release()
1193 # if there is a short address than we can send the message
1194 # if there is not one then we cannot since we need both the short and
1196 if(shortAddr != None):
1198 # get a request number
1199 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1202 if(seqNumber == -1):
1203 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1206 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1207 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1208 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1209 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1210 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1212 # create the payload data
1214 payloadData += '\x00'
1215 payloadData += chr(seqNumber)
1216 payloadData += '\x06'
1217 payloadData += '\x00'
1218 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1219 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1220 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1221 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1223 if(parsedData.has_key('reportable_change')):
1224 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1227 # create and send binding command
1228 zigbeeConnectionMutex.acquire()
1229 zigbeeConnection.send('tx_explicit',
1231 # frame_id=chr(seqNumber),
1232 dest_addr_long=destLongAddr,
1233 dest_addr=destShortAddr,
1234 src_endpoint='\x00',
1235 dest_endpoint=dstEndpoint,
1240 zigbeeConnectionMutex.release()
1244 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1247 def processUdpPolicySet(parsedData, addr):
1248 ''' Method handle a policy set message
1250 parsedData -- Pre-parsed Data that was in the UDP packet.
1251 addr -- Address (IP and Port) of the UDP packet origin.
1253 print "=================================================================="
1254 print "Policy set: ", parsedData
1255 print 'addr : ', addr
1258 # do nothing if wrong source
1259 if addr == SYSTEM_MASTER_ADDRESS :
1260 key = (parsedData['ip_address'], int(parsedData['port']))
1261 if (zigbeeAddressAuthorityDict.has_key(key)):
1262 zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1264 zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1267 def processUdpPolicyClear(parsedData, addr):
1268 ''' Method handle a policy set message
1270 parsedData -- Pre-parsed Data that was in the UDP packet.
1271 addr -- Address (IP and Port) of the UDP packet origin.
1273 print "=================================================================="
1274 print "Clear policy: ", parsedData
1276 # do nothing if wrong source
1277 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1278 if addr == SYSTEM_MASTER_ADDRESS :
1279 zigbeeAddressAuthorityDict.clear()
1286 def processZigbeeATCommandMessage(parsedData):
1287 ''' Method to process an AT zigbee message
1289 parsedData -- Pre-parsed (into a dict) data from message.
1291 global ZIGBEE_DEVICE_ADDRESS
1292 global didGetLocalRadioHighAddress
1293 global didGetLocalRadioLowAddress
1295 # command response for the high bytes of the local device long address
1296 if(parsedData['command'] == 'SH'):
1297 # convert the parameter to a string value (human readable)
1299 for e in parsedData['parameter']:
1300 value += "{0:02x}".format(ord(e))
1302 # set the correct portion of the address
1303 ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1305 #signal that we got this part of the address
1306 didGetLocalRadioHighAddress = True
1308 # command response for the low bytes of the local device long address
1309 elif(parsedData['command'] == 'SL'):
1310 # convert the parameter to a string value (human readable)
1312 for e in parsedData['parameter']:
1313 value += "{0:02x}".format(ord(e))
1315 # set the correct portion of the address
1316 ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1318 #signal that we got this part of the address
1319 didGetLocalRadioLowAddress = True
1321 def processZigbeeRxExplicitCommandMessage(parsedData):
1322 ''' Method to process a rx-explicit zigbee message
1324 parsedData -- Pre-parsed (into a dict) data from message.
1326 global zigbeeBindRequestMutex
1327 global zigbeeBindRequest
1328 global zigbeeConnectionMutex
1329 global zigbeeConnection
1330 global ManagementPermitJoiningReqSuccess
1332 # get the long and short addresses from the message payload since we can
1333 # use these to update the short addresses since this short address is fresh
1334 longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1335 shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1337 # check if this short address is for a device that has yet to be
1339 zigbeeUnregisteredAddressesMutex.acquire()
1340 if(longAddr in zigbeeUnregisteredAddresses):
1341 zigbeeUnregisteredAddresses.remove(longAddr)
1342 zigbeeUnregisteredAddressesMutex.release()
1344 # update/ or insert the short address
1345 zigbeeLongShortAddrMutex.acquire()
1346 zigbeeLongShortAddr[longAddr] = shortAddr
1347 zigbeeLongShortAddrMutex.release()
1349 global matchDescriptorReqSingleton
1350 global deviceAnnouncementSingleton
1351 global seqNumberForNotification
1355 if (parsedData['cluster'] == '\x01\x01' and parsedData['profile'] == '\x01\x04'):
1356 zclSeqNumber = parsedData['rf_data'][1]
1358 zigbeeSeqNumberToClientMutex.acquire()
1359 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1360 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1361 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1362 zigbeeSeqNumberToClientMutex.release()
1364 rfdata = parsedData['rf_data']
1365 framecontrol = rfdata[0]
1371 if framecontrol == '\x19':
1372 if(command == '\x00'):
1374 print "( 0x0101 ) Door Lock: Lock Door Response"
1375 print time.strftime("%H:%M:%S", time.localtime())
1377 if(value == '\x00'):
1378 print "Door locked successfully"
1380 print "An error occurred in door locking"
1381 elif(command == '\x01'):
1383 print "( 0x0101 ) Door Lock: Unlock Door Response"
1384 print time.strftime("%H:%M:%S", time.localtime())
1386 if(value == '\x00'):
1387 print "Door unlocked successfully"
1389 print "An error occurred in door unlocking"
1391 elif framecontrol == '\x18':
1392 if(command == '\x01'):
1393 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1394 if attributeId == 0x0000:
1396 print "Door status: "
1398 print "Not fully locked"
1399 elif value == '\x01':
1401 elif value == '\x02':
1404 print "Unknown value: " + zigbeeHexStringToHexString(value)
1406 message = "type : zcl_read_attributes_response \n"
1407 message += "packet_id: " + packetId + "\n"
1408 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1409 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1410 message += "attributes: "
1412 attrIdStr = "%0.4x" % attributeId
1413 attrIdStr = changeEndian(attrIdStr)
1414 message += attrIdStr
1417 zclPayload = parsedData['rf_data'][3:]
1418 zclPayload = zclPayload[3:]
1419 attributeType = zclPayload[0]
1420 message += "%0.2x" % ord(attributeType)
1423 message += "success"
1426 message += "%0.2x" % ord(value)
1431 message = message[0:len(message) - 1]
1434 # no one to send the response to so just move on
1436 # cant really do anything here
1438 sendSocket.sendto(message,tup[0])
1439 elif command == '\x07':
1442 print "( 0x0101 ) Door Lock: Configure reporting response"
1443 print 'rfdata : ' + zigbeeHexStringToHexString(rfdata)
1444 if status == '\x00':
1445 print "Configure report successfully"
1446 message = "type : zcl_configure_reporting_response \n"
1447 message += "packet_id: " + packetId + "\n"
1448 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1449 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1450 message += "attributes: "
1451 message += "all_success \n";
1453 # no one to send the response to so just move on
1455 # cant really do anything here
1457 sendSocket.sendto(message,tup[0])
1459 print "Configure report unsuccessfully, status =", zigbeeHexStringToHexString(status)
1460 elif(command == '\x0A'):
1462 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1463 if attributeId == 0x0000:
1466 print "Not fully locked"
1467 elif value == '\x01':
1469 elif value == '\x02':
1472 print "Unknown value: " + zigbeeHexStringToHexString(value)
1474 message = "type : zcl_read_attributes_response \n"
1475 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1476 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1477 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1478 message += "attributes: "
1480 attrIdStr = "%0.4x" % attributeId
1481 attrIdStr = changeEndian(attrIdStr)
1482 message += attrIdStr
1485 zclPayload = parsedData['rf_data'][3:]
1486 zclPayload = zclPayload[3:]
1487 attributeType = zclPayload[0]
1488 message += "%0.2x" % ord(attributeType)
1491 message += "success"
1494 message += "%0.2x" % ord(value)
1499 message = message[0:len(message) - 1]
1502 # get callback clients to respond to
1503 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1505 zigbeeHACallbackMutex.acquire()
1506 if(zibeeHACallback.has_key(callbackIndex)):
1507 retAddr = zibeeHACallback[callbackIndex]
1508 zigbeeHACallbackMutex.release()
1510 # no one to respond to so do nothing here
1511 if(retAddr == None):
1514 sendSocket.sendto(message,ra)
1517 # if this is a ZDO message/response
1518 if(parsedData['profile'] == '\x00\x00'):
1521 # if this is a Match Descriptor Request so we need to answer.
1522 if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1523 zigbeeConnectionMutex.acquire()
1524 zigbeeConnection.send('tx_explicit',
1526 # frame_id=chr(seqNumber),
1527 dest_addr_long=parsedData['source_addr_long'],
1528 dest_addr=parsedData['source_addr'],
1529 src_endpoint='\x00',
1530 dest_endpoint='\x00',
1533 data=parsedData['rf_data']
1536 zigbeeConnection.send('tx_explicit',
1538 # frame_id=chr(seqNumber),
1539 dest_addr_long=parsedData['source_addr_long'],
1540 dest_addr=parsedData['source_addr'],
1541 src_endpoint='\x00',
1542 dest_endpoint='\x00',
1545 data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1549 print '[ 0x0006 ] Match Descriptor Request - answered'
1550 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1551 zigbeeConnectionMutex.release()
1554 # if this is a device announcement so we can get some useful data from it
1555 elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1556 # pick out the correct parts of the payload
1557 longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1558 shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1560 # change the endian of the address
1561 longAddr = changeEndian(longAddr)
1562 shortAddr = changeEndian(shortAddr)
1564 # update the table with the new information
1565 zigbeeLongShortAddrMutex.acquire()
1566 zigbeeLongShortAddr[longAddr] = shortAddr
1567 zigbeeLongShortAddrMutex.release()
1569 # check if this short address is for a device that has yet to be
1571 zigbeeUnregisteredAddressesMutex.acquire()
1572 if(longAddr in zigbeeUnregisteredAddresses):
1573 zigbeeUnregisteredAddresses.remove(longAddr)
1574 zigbeeUnregisteredAddressesMutex.release()
1577 zigbeeConnectionMutex.acquire()
1578 zigbeeConnection.send('tx_explicit',
1580 # frame_id=chr(seqNumber),
1581 dest_addr_long=parsedData['source_addr_long'],
1582 dest_addr=parsedData['source_addr'],
1583 src_endpoint='\x00',
1584 dest_endpoint='\x00',
1587 data=parsedData['rf_data']
1590 print '[ 0x0013 ] device announcement - answered'
1591 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1592 deviceAnnouncementSingleton = False
1593 zigbeeConnectionMutex.release()
1596 # if this is a response to a zdo bind_req message
1597 elif(parsedData['cluster'] == '\x80\x21'):
1599 # get the status and sequence number from the message
1600 seqNumber = parsedData['rf_data'][0]
1601 statusCode = parsedData['rf_data'][1]
1602 print "> response to a zdo bind_req message parsedData"
1604 # get the bind tuple information
1605 # for this specific bind request
1607 zigbeeBindRequestMutex.acquire()
1608 if(zigbeeBindRequest.has_key(ord(seqNumber))):
1609 tup = zigbeeBindRequest[ord(seqNumber)]
1610 zigbeeBindRequestMutex.release()
1613 # cant really do anything in this case...
1614 # don't have any information on who the data is for
1617 # successful binding
1618 if(ord(statusCode) == 0):
1620 # add a callback for this specific device and cluster
1621 # to the HA callback dict
1622 zigbeeHACallbackMutex.acquire();
1623 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1624 if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1625 zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1627 zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1628 zigbeeHACallbackMutex.release()
1630 # send success message
1631 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1633 print "> Success message sent!"
1636 elif (ord(statusCode) == 170):
1637 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1640 elif (ord(statusCode) == 174):
1641 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1643 # Other issue, dont have code for
1645 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1647 # if this is a response to a short address query
1648 elif(parsedData['cluster'] == '\x80\x00'):
1649 #print ">response to a short address query 0x8000"
1652 statusCode = parsedData['rf_data'][0]
1654 # does not matter if this is not a success, we can try again later
1655 if(statusCode != '\x00'):
1656 # status code was not success so do not do anything
1659 # get the short and long address information
1660 longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1661 shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1663 # remove device from list of unregistered devices if it is in it
1664 zigbeeUnregisteredAddressesMutex.acquire()
1665 if(longAddr in zigbeeUnregisteredAddresses):
1666 zigbeeUnregisteredAddresses.remove(longAddr)
1667 zigbeeUnregisteredAddressesMutex.release()
1669 # update/insert the short address
1670 zigbeeLongShortAddrMutex.acquire()
1671 zigbeeLongShortAddr[longAddr] = shortAddr
1672 zigbeeLongShortAddrMutex.release()
1675 elif(parsedData['cluster'] == '\x80\x06'):
1677 print '[ 0x8006 ] get Match Descriptor Response'
1678 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1681 elif(parsedData['cluster'] == '\x80\x36'):
1683 print '[ 0x8036 ] get Management Permit Joining Response'
1684 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1686 ManagementPermitJoiningReqSuccess = True
1691 print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1692 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1695 # if this is a home automation zcl message/response
1696 elif (parsedData['profile'] == '\x01\x04'):
1698 # get the zcl message header
1699 zclFrameControl = parsedData['rf_data'][0]
1700 zclSeqNumber = parsedData['rf_data'][1]
1701 zclCommand = parsedData['rf_data'][2]
1702 zclStatus = parsedData['rf_data'][3]
1705 if(zclCommand == '\x00'):
1707 print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1709 # find who to send response
1711 zigbeeSeqNumberToClientMutex.acquire()
1713 if(longAddr in seqNumberForNotification):
1715 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1716 tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1717 #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1718 zigbeeSeqNumberToClientMutex.release()
1720 # no one to send the response to so just move on
1722 # cant really do anything here
1724 # create the response message
1726 message = "type : zcl_zone_status_change_notification\n"
1727 message += "packet_id: " + packetId + "\n"
1728 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1729 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1730 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1731 message += "attributes: success"
1734 sendSocket.sendto(message,tup[0])
1735 print(">port : ", tup[0][1])
1739 # this is a zcl read attribute response
1740 elif(zclCommand == '\x01'):
1742 # get the zcl payload
1743 zclPayload = parsedData['rf_data'][3:]
1744 attibuteResponseList = []
1746 # get the data for each data
1747 while(len(zclPayload) > 0):
1748 attributeId = zclPayload[0:2]
1749 attributeStatus = zclPayload[2]
1750 zclPayload = zclPayload[3:]
1752 if(ord(attributeStatus) != 0):
1753 # if attribute is not supported then it has no data
1754 # package the data and add it to the list
1755 attibuteResponseList.append((attributeId,"not_supported"))
1758 # get the data type and data length of the attributre
1759 attributeType = zclPayload[0]
1760 dataLength = zclDataTypeToBytes(zclPayload)
1762 # consume zcl payload data
1763 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1764 zclPayload = zclPayload[2:]
1765 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1766 zclPayload = zclPayload[3:]
1768 zclPayload = zclPayload[1:]
1770 # package the data and add it to the list
1771 newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1772 attibuteResponseList.append(newData)
1774 # consume the data size of the payload
1775 zclPayload = zclPayload[dataLength:]
1777 # find who to send response to
1779 zigbeeSeqNumberToClientMutex.acquire()
1780 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1781 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1782 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1783 zigbeeSeqNumberToClientMutex.release()
1785 # no one to send the response to so just move on
1787 # cant really do anything here
1790 # create the response message
1792 message = "type : zcl_read_attributes_response \n"
1793 message += "packet_id: " + packetId + "\n"
1794 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1795 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1796 message += "attributes: "
1798 # create the message for each attribute
1799 for t in attibuteResponseList:
1800 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1801 if(t[1] == "success"):
1802 attrIdStr = "%0.4x" % attrId
1803 attrIdStr = changeEndian(attrIdStr)
1805 message += attrIdStr
1807 message += "success"
1809 message += "%0.2x" % ord(t[2])
1814 dat += "%0.2x" % ord(c)
1815 dat = changeEndian(dat)
1819 attrIdStr = "%0.4x" % attrId
1820 attrIdStr = changeEndian(attrIdStr)
1822 message += attrIdStr
1824 message += "not_supported"
1827 message = message[0:len(message) - 1]
1830 sendSocket.sendto(message,tup[0])
1833 # this is a zcl write attribute response
1834 elif(zclCommand == '\x04'):
1836 # get the zcl payload
1837 zclPayload = parsedData['rf_data'][3]
1838 # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1839 if(zclStatus == '\x70' or zclPayload == '\x00'):
1841 # find who to send response to
1843 zigbeeSeqNumberToClientMutex.acquire()
1844 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1845 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1846 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1847 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1848 zigbeeSeqNumberToClientMutex.release()
1849 # no one to send the response to so just move on
1851 # cant really do anything here
1854 # create the response message
1856 message = "type : zcl_write_attributes_response\n"
1857 message += "packet_id: " + packetId + "\n"
1858 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1859 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1860 message += "attributes: success"
1863 sendSocket.sendto(message,tup[0])
1865 print '[ 0x0500 ] get Write Attribute Response success'
1866 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1870 print '[ 0x0500 ] get Write Attribute Response'
1871 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1875 # this is a zcl configure attribute response
1876 elif(zclCommand == '\x07'):
1878 # find who to send response to
1880 zigbeeSeqNumberToClientMutex.acquire()
1881 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1882 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1883 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1884 zigbeeSeqNumberToClientMutex.release()
1886 # no one to send the response to so just move on
1888 # cant really do anything here
1892 zclPayload = parsedData['rf_data'][3:]
1894 # construct the message
1896 message = "type : zcl_configure_reporting_response \n"
1897 message += "packet_id: " + packetId + "\n"
1898 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1899 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1900 message += "attributes: "
1902 if(len(zclPayload) == 1):
1903 # if all the configurations are a success then only send back a success
1904 # based on zigbee specs
1905 message += "all_success \n";
1906 sendSocket.sendto(message,tup[0])
1909 attibuteResponseList = []
1911 # get each attributes data
1912 while(len(zclPayload) > 0):
1913 attributeStatus = zclPayload[0]
1914 attributeDirection = zclPayload[1]
1915 attributeId = zclPayload[2:4]
1916 zclPayload = zclPayload[4:]
1918 newData = (attributeStatus,attributeDirection, attributeId)
1919 attibuteResponseList.append(newData)
1921 # package each attribute
1922 for t in attibuteResponseList:
1923 attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1924 attrIdStr = "%0.4x" % attrId
1925 attrIdStr = changeEndian(attrIdStr)
1927 message += attrIdStr
1930 message += "success"
1937 message += "reported"
1939 message += "received"
1942 message = message[0:len(message) - 1]
1944 sendSocket.sendto(message,tup[0])
1946 # this is a zcl report attribute message
1947 elif(zclCommand == '\x0a'):
1948 print "get Report attribute "
1949 # get teh zcl payload
1950 zclPayload = parsedData['rf_data'][3:]
1951 attibuteResponseList = []
1953 # extract the attribute data
1954 while(len(zclPayload) > 0):
1955 attributeId = zclPayload[0:2]
1956 zclPayload = zclPayload[2:]
1957 attributeType = zclPayload[0]
1958 dataLength = zclDataTypeToBytes(zclPayload)
1960 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1961 zclPayload = zclPayload[2:]
1962 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1963 zclPayload = zclPayload[3:]
1965 zclPayload = zclPayload[1:]
1967 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1968 attibuteResponseList.append(newData)
1969 zclPayload = zclPayload[dataLength:]
1972 # get callback clients to respond to
1973 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1975 zigbeeHACallbackMutex.acquire()
1976 if(zibeeHACallback.has_key(callbackIndex)):
1977 retAddr = zibeeHACallback[callbackIndex]
1978 zigbeeHACallbackMutex.release()
1980 # no one to respond to so do nothing here
1981 if(retAddr == None):
1984 # construct the message
1985 message = "type : zcl_report_attributes \n"
1986 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1987 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1988 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1989 message += "attributes: "
1991 # package the attributes
1992 for t in attibuteResponseList:
1993 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1994 attrIdStr = "%0.4x" % attrId
1995 attrIdStr = changeEndian(attrIdStr)
1997 message += attrIdStr
1999 message += "%0.2x" % ord(t[1])
2004 dat += "%0.2x" % ord(c)
2005 dat = changeEndian(dat)
2009 message = message[0:len(message) - 1]
2011 print "Sending", message
2013 # send to all client that want this callback
2015 sendSocket.sendto(message,ra)
2017 # -----------------------------------------------------------------------------
2018 # Communication Callback/Parse Methods
2019 # -----------------------------------------------------------------------------
2020 def handleNewZigbeeMessage(parsedData):
2021 ''' Method to process a zigbee message from the local radio.
2023 parsedData -- Pre-parsed (into a dict) data from message.
2025 #print "=================================================================="
2027 #print "New Zigbee Message"
2028 #printMessageData(parsedData)
2030 # dispatch to the correct zigbee handler
2031 if (parsedData['id'] == 'at_response'):
2032 #print "parsedDataID : at_response"
2033 processZigbeeATCommandMessage(parsedData)
2035 elif (parsedData['id'] == 'rx_explicit'):
2036 #print "parsedDataID : rx_explicit"
2037 processZigbeeRxExplicitCommandMessage(parsedData)
2040 #print "Unknown API format"
2042 #print "=================================================================="
2046 def handleNewUdpPacket(data, addr):
2047 ''' Method to parse and handle an incoming UDP packet.
2049 data -- Data that was in the UDP packet.
2050 addr -- Address (IP and Port) of the UDP packet origin.
2052 global ManagementPermitJoiningReqSuccess
2054 #print "=================================================================="
2056 #print "Got New UDP packet..."
2060 # data comes in as 'key: value\n key: value...' string and so needs to be
2061 # parsed into a dict
2064 # 1 key, value pair per line
2065 for line in data.split('\n'):
2067 # key and values are split based on a ':'
2068 fields = line.split(':')
2070 # make sure properly formated otherwise just ignore it
2071 if len(fields) == 2:
2073 # do strips to remove any white spacing that may have resulted
2074 # from improper packing on the sender side
2075 parsedData[fields[0].strip()] = fields[1].strip()
2078 # wrap in try statement just in case there is an improperly formated packet we
2081 # dispatch to the correct process method
2082 if(parsedData["type"] == "zdo_bind_request"):
2083 print "> processUdpZdoBindReqMessage call"
2084 processUdpZdoBindReqMessage(parsedData, addr)
2085 elif(parsedData["type"] == "zdo_unbind_request"):
2086 processUdpZdoUnBindReqMessage(parsedData, addr)
2087 elif(parsedData["type"] == "send_address"):
2088 print "> processUdpSendAddressMessage call"
2089 processUdpSendAddressMessage(parsedData, addr)
2090 elif(parsedData["type"] == "zcl_read_attributes"):
2091 processUdpZclReadAttributesMessage(parsedData, addr)
2092 elif(parsedData["type"] == "zcl_configure_reporting"):
2093 print "> processUdpZclConfigureReportingMessage call"
2094 processUdpZclConfigureReportingMessage(parsedData, addr)
2095 elif(parsedData["type"] == "policy_set"):
2096 processUdpPolicySet(parsedData, addr)
2097 elif(parsedData["type"] == "policy_clear"):
2098 processUdpPolicyClear(parsedData, addr)
2099 elif(parsedData["type"] == "management_permit_joining_request"): # Added by Changwoo
2100 processUdpManagementPermitJoiningReqMessage(parsedData, addr)
2101 elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): # Added by Changwoo
2102 processUdpZclWriteAttributesMessage(parsedData, addr)
2103 elif(parsedData["type"] == "zcl_enrollment_response"): # Added by Changwoo
2104 processUdpEnrollmentResponse(parsedData, addr)
2105 elif(parsedData["type"] == "zdo_broadcast_route_record_request"): # Added by Changwoo
2106 processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
2107 elif(parsedData["type"] == "zcl_change_switch_request"): # Added by Changwoo
2108 processUdpZclChangeSwitchReqMessage(parsedData, addr)
2109 elif(parsedData["type"] == "zcl_lock_or_unlock_door_request"): # Added by Jiawei
2110 processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr)
2111 elif(parsedData["type"] == "zcl_read_door_status_request"): # Added by Jiawei
2112 processUdpZclReadDoorStatusReqMessage(parsedData, addr)
2114 #print "unknown Packet: " + parsedData["type"]
2117 # if we ever get here then something went wrong and so just ignore this
2118 # packet and try again later
2119 print "I didn't expect this error:", sys.exc_info()[0]
2120 traceback.print_exc()
2122 #print "=================================================================="
2125 # -----------------------------------------------------------------------------
2126 # Main Running Methods
2127 # -----------------------------------------------------------------------------
2130 '''Main function used for starting the application as the main driver'''
2132 global ZIGBEE_SERIAL_PORT
2133 global ZIGBEE_SERIAL_BAUD
2134 global UDP_RECEIVE_PORT
2135 global zigbeeConnection
2139 parseCommandLineArgs(sys.argv[1:])
2141 # create serial object used for communication to the zigbee radio
2142 sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
2144 # create a zigbee object that handles all zigbee communication
2145 # we use this to do all communication to and from the radio
2146 # when data comes from the radio it will get a bit of unpacking
2147 # and then a call to the callback specified will be done with the
2149 zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
2151 # get the long address of our local radio before we start doing anything
2152 getConnectedRadioLongAddress();
2154 # setup incoming UDP socket and bind it to self and specified UDP port
2155 # sending socket does not need to be bound to anything
2156 receiveSocket.bind((LOCAL_ADDRESS, UDP_RECEIVE_PORT))
2158 # create the thread that does short address lookups
2159 addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
2160 addressUpdateWorkerThread.start()
2165 print "=================================================================="
2168 print "=================================================================="
2170 # wait for an incoming UDP packet
2171 # this is a blocking call
2172 data, addr = receiveSocket.recvfrom(4096)
2174 # handle the UDP packet appropriately
2175 handleNewUdpPacket(data, addr)
2177 except KeyboardInterrupt:
2178 # use the keyboard interupt to catch a ctrl-c and kill the application
2182 # something went really wrong and so exit with error message
2183 traceback.print_exc()
2185 # signal all threads to exit
2188 # wait for threads to finish before closing of the resources
2189 addressUpdateWorkerThread.join()
2192 # make sure to close all the connections
2193 zigbeeConnection.halt()
2194 receiveSocket.close()
2197 if __name__ == "__main__":
2198 # call main function since this is being run as the start