1 from xbee import ZigBee
9 from threading import Thread, Lock
14 # -----------------------------------------------------------------------------
15 # Constants ans Pseudo-Constants
16 # -----------------------------------------------------------------------------
17 UDP_RECEIVE_PORT = 5005 # port used for incoming UDP data
18 UDP_RECEIVE_BUFFER_SIZE = 4096 # max buffer size of an incoming UDP packet
19 SYSTEM_MASTER_ADDRESS = ("127.0.0.1", 12345) # ip address and portof the system master node
20 #SYSTEM_MASTER_ADDRESS = ("127.0.0.1", 22222) # ip address and portof the system master node
21 #SYSTEM_MASTER_ADDRESS2 = ("127.0.0.1", 11111)
22 #SYSTEM_MASTER_ADDRESS3 = ("127.0.0.1", 11222)
24 # time for messages to wait for a response before the system clears away that
26 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5
28 #ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH" # USB-Serial port of local radio
29 ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
30 ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port
32 # address of our local zigbee radio
33 ZIGBEE_DEVICE_ADDRESS = "0013a20040d99cb4"
35 # -----------------------------------------------------------------------------
36 # Global Variables and Objects
37 # -----------------------------------------------------------------------------
39 # signals to see if a request needs to be made
40 didGetLocalRadioHighAddress = False;
41 didGetLocalRadioLowAddress = False;
43 # zigbee communications object and its mutex
44 zigbeeConnection = None
45 zigbeeConnectionMutex = Lock()
47 #singleton mabe by changwoo
48 matchDescriptorReqSingleton = True
49 deviceAnnouncementSingleton = True
50 ManagementPermitJoiningReqSuccess = False
52 # zigbee mapping from long to short object dict
53 zigbeeLongShortAddr = dict()
54 zigbeeLongShortAddrMutex = Lock()
56 # zigbee mapping from a sequence number to a client
57 # for correct response handling
58 zigbeeSeqNumberToClient = dict()
59 zigbeeSeqNumberToClientMutex = Lock()
61 zigeeBindRequest = dict()
62 zigeeBindRequestMutex = Lock()
64 # Keeps record of where to send callbacks to when an HA message is received
65 zibeeHACallback = dict()
66 zibeeHACallbackMutex = Lock()
69 # Keeps a record of device addresses whose short addresses have not been
71 zigbeeUnregisteredAddresses = []
72 zigbeeUnregisteredAddressesMutex = Lock()
74 # used to signal all threads to end
78 # 2 sockets, one for sending (not bound to a port manually)
79 # and one for receiving, known port binding by application
81 sendSoceket = socket(AF_INET, SOCK_DGRAM)
82 receiveSoceket = socket(AF_INET, SOCK_DGRAM)
85 # zigbee address authority list
86 zigbeeAddressAuthorityDict = dict()
89 seqNumberForNotification = dict()
91 # -----------------------------------------------------------------------------
93 # -----------------------------------------------------------------------------
94 def reverseShortAddress(shortAddr):
95 result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
98 def parseCommandLineArgs(argv):
99 global ZIGBEE_SERIAL_PORT
100 global ZIGBEE_SERIAL_BAUD
102 opts, args = getopt.getopt(
103 argv, "hp:b:u:", ["port=", "baud=", "udpport="])
105 except getopt.GetoptError:
106 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
109 for opt, arg in opts:
111 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
114 elif opt in ("-p", "--port"):
115 ZIGBEE_SERIAL_PORT = arg
117 elif opt in ("-b", "--baud"):
119 ZIGBEE_SERIAL_BAUD = int(arg)
121 print "Buad rate must be an integer"
126 # Convenience (Stateless)
129 def hexListToChar(hexList):
130 ''' Method to convert a list/string of characters into their corresponding values
132 hexList -- list or string of hex characters
136 retString += chr(int(h, 16))
139 def splitByN(seq, n):
140 ''' Method to split a string into groups of n characters
145 return [seq[i:i+n] for i in range(0, len(seq), n)]
147 def changeEndian(hexString):
148 ''' Method to change endian of a hex string
150 hexList -- string of hex characters
152 split = splitByN(hexString, 2) # get each byte
153 split.reverse(); # reverse ordering of the bytes
161 def printMessageData(data):
162 ''' Method to print a zigbee message to the console
164 data -- pre-parsed zigbee message
169 print "{0:02x}".format(ord(e)),
171 print "({})".format(data[d]),
174 def hexStringToZigbeeHexString(hexString):
175 ''' Method to change a hex string to a string of characters with the hex values
177 hexList -- string of hex characters
179 return hexListToChar(splitByN(hexString, 2))
181 def zigbeeHexStringToHexString(zigbeeHexString):
182 ''' Method to change string of characters with the hex values to a hex string
184 hexList -- string of characters with hex values
188 for e in zigbeeHexString:
189 retString += "{0:02x}".format(ord(e))
192 def zclDataTypeToBytes(zclPayload):
193 ''' Method to determine data length of a zcl attribute
195 zclPayload -- ZCL payload data, must have dataType as first byte
197 attrType = ord(zclPayload[0])
199 if(attrType == 0x00):
201 elif (attrType == 0x08):
203 elif (attrType == 0x09):
205 elif (attrType == 0x0a):
207 elif (attrType == 0x0b):
209 elif (attrType == 0x0c):
211 elif (attrType == 0x0d):
213 elif (attrType == 0x0e):
215 elif (attrType == 0x0f):
217 elif (attrType == 0x10):
219 elif (attrType == 0x18):
221 elif (attrType == 0x19):
223 elif (attrType == 0x1a):
225 elif (attrType == 0x1b):
227 elif (attrType == 0x1c):
229 elif (attrType == 0x1d):
231 elif (attrType == 0x1e):
233 elif (attrType == 0x1f):
235 elif (attrType == 0x20):
237 elif (attrType == 0x21):
239 elif (attrType == 0x22):
241 elif (attrType == 0x23):
243 elif (attrType == 0x24):
245 elif (attrType == 0x25):
247 elif (attrType == 0x26):
249 elif (attrType == 0x27):
251 elif (attrType == 0x28):
253 elif (attrType == 0x29):
255 elif (attrType == 0x2a):
257 elif (attrType == 0x2b):
259 elif (attrType == 0x2c):
261 elif (attrType == 0x2d):
263 elif (attrType == 0x2e):
265 elif (attrType == 0x2f):
267 elif (attrType == 0x30):
269 elif (attrType == 0x31):
271 elif (attrType == 0x38):
273 elif (attrType == 0x39):
275 elif (attrType == 0x3a):
277 elif (attrType == 0x41):
278 return ord(zclPayload[1])
279 elif (attrType == 0x42):
280 return ord(zclPayload[1])
281 elif (attrType == 0x43):
282 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
283 elif (attrType == 0x44):
284 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
285 elif (attrType == 0xe0):
287 elif (attrType == 0xe1):
289 elif (attrType == 0xe2):
291 elif (attrType == 0xe8):
293 elif (attrType == 0xe9):
295 elif (attrType == 0xea):
297 elif (attrType == 0xf0):
299 elif (attrType == 0xf1):
301 elif (attrType == 0xff):
308 def createSequenceNumberForClient(addr, packetId):
309 ''' Method to get and store a sequence number with a specific client
310 for a specific message.
312 addr -- UDP address of the client to associate with the seq. number
313 packetId -- packet id from the UDP packet
315 # keep trying to find a number to return
318 # get the current time
319 epoch_time = int(time.time())
321 # get the current list of used numbers
322 zigbeeSeqNumberToClientMutex.acquire()
323 keysList = zigbeeSeqNumberToClient.keys()
324 zigbeeSeqNumberToClientMutex.release()
326 # if all the numbers are already used
327 if(len(keysList) == 256):
329 # get a list of all the items
330 zigbeeSeqNumberToClientMutex.acquire()
331 itemsList = zigbeeSeqNumberToClient.items()
332 zigbeeSeqNumberToClientMutex.release()
334 # search for a number that is old enough to get rid of otherwise use -1
336 for item in itemsList:
337 if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC):
342 # replace the record with new data if we found one to replace
343 zigbeeSeqNumberToClientMutex.acquire()
344 zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId)
345 zigbeeSeqNumberToClientMutex.release()
350 # not all numbers used yet so pick one randomly
351 randNum = random.randrange(0,256)
353 # check if we are using the number yet
354 if(randNum not in keysList):
356 # we are not so insert to keep track who this number is for and return it
357 zigbeeSeqNumberToClientMutex.acquire()
358 zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId)
359 zigbeeSeqNumberToClientMutex.release()
362 def getConnectedRadioLongAddress():
363 """ Method to make sure we get the MAC address of our local radio"""
364 global zigbeeConnection
367 # keep looping until we get both the MSBs and the LSBs
368 while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)):
371 zigbeeConnection.send('at', command="SH")
372 zigbeeConnection.send('at', command="SL")
374 # sleep for a bit to give the radio time to respond before we check again
377 def addressUpdateWorkerMethod():
378 ''' Method to keep refreshing the short addresses of the known zigbee devices'''
380 global zigbeeLongShortAddr
381 global zigbeeLongShortAddrMutex
382 global zigbeeUnregisteredAddresses
383 global zigbeeUnregisteredAddressesMutex
384 global zigbeeConnectionMutex
385 global zigbeeConnection
387 # keep looping until signaled to quit
388 while(not doEndFlag):
392 # add unregistered (short addresses unknown) devices so
393 # that we can look them up
394 zigbeeUnregisteredAddressesMutex.acquire()
395 addrList.extend(zigbeeUnregisteredAddresses)
396 zigbeeUnregisteredAddressesMutex.release()
398 # add the devices that we have short addresses for so we can
399 # get their most recent short addresses
400 zigbeeLongShortAddrMutex.acquire()
401 addrList.extend(zigbeeLongShortAddr.keys())
402 zigbeeLongShortAddrMutex.release()
404 # Loop through all the addresses and send messages for each address
407 # create payload for a query on the network for a short address
409 payload += hexStringToZigbeeHexString(changeEndian(ad))
412 # create and send binding command
413 zigbeeConnectionMutex.acquire()
415 zigbeeConnection.send('tx_explicit',
417 dest_addr_long=hexStringToZigbeeHexString(ad),
418 dest_addr='\xff\xfd',
420 dest_endpoint='\x00',
425 zigbeeConnectionMutex.release()
434 def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
435 ''' Method to send a success or fail back to a client.
437 addr -- UDP address to send packet to
438 packetTypeStr -- name of this specific packet
439 packetIdStr -- packet id to send
440 sucOrFail -- whether this is a success or fail message (True = success)
441 reason -- reason of failure (if needed, default is None)
447 # construct the message
448 message = "type: " + packetTypeStr.strip() + "\n"
449 message += "packet_id: " + packetIdStr + "\n"
452 message += "response: success \n"
454 message += "response: fail \n"
455 message += "reason: " + reason + "\n"
457 # send message in a UDP packet
458 sendSoceket.sendto(message,addr)
460 def processUdpZdoBindReqMessage(parsedData, addr):
464 if(zigbeeAddressAuthorityDict.has_key(addr)):
465 l = zigbeeAddressAuthorityDict[addr]
466 if(parsedData['device_address_long'] not in l):
471 # get the short address for this device long address if possible
472 zigbeeLongShortAddrMutex.acquire()
473 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
474 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
475 zigbeeLongShortAddrMutex.release()
477 # if there is a short address than we can send the message
478 # if there is not one then we cannot since we need both the short and
480 if(shortAddr != None):
482 # get a request number
483 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
488 # send an error message, could not get a sequence number to use at this time
489 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
492 # a bind request was made so must store and wait for response
493 # before we setup callbacks, so keep just the data we need to create the callback
494 zigeeBindRequestMutex.acquire()
495 zigeeBindRequest[seqNumber] = (parsedData['device_address_long'],
496 parsedData['cluster_id'],
497 parsedData['packet_id'],
499 zigeeBindRequestMutex.release()
501 # construct the short and long addresses of the message for sending
502 # make sure they are in the correct format
503 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
504 destShortAddr = hexStringToZigbeeHexString(shortAddr)
506 # create the payload data
508 payloadData += chr(seqNumber)
509 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
510 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
511 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id']))
512 payloadData += '\x03'
513 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
514 payloadData += '\x00'
516 # create and send binding command
517 zigbeeConnectionMutex.acquire()
518 zigbeeConnection.send('tx_explicit',
520 # frame_id=chr(seqNumber),
521 dest_addr_long=destLongAddr,
522 dest_addr=destShortAddr,
524 dest_endpoint='\x00',
529 zigbeeConnectionMutex.release()
533 # send a failure packet since there is no short address available
534 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
537 def processUdpZdoUnBindReqMessage(parsedData, addr):
538 zibeeHACallbackMutex.acquire();
539 if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
540 zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
541 zibeeHACallbackMutex.release()
542 sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
546 def processUdpSendAddressMessage(parsedData, addr):
547 ''' Method handle a send address command
549 parsedData -- Pre-parsed Data that was in the UDP packet.
550 addr -- Address (IP and Port) of the UDP packet origin.
552 global zigbeeLongShortAddr
553 global zigbeeLongShortAddrMutex
554 global zigbeeUnregisteredAddresses
555 global zigbeeUnregisteredAddressesMutex
558 print "process send address"
561 # construct success message
562 message = "type: send_address_response\n"
563 message += "packet_id: " + parsedData['packet_id'] + "\n"
564 message += "response: success\n"
566 # tell client that we got their request
567 sendSoceket.sendto(message,addr)
568 print "responding", message
571 zigbeeLongShortAddrMutex.acquire()
572 doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
573 zigbeeLongShortAddrMutex.release()
576 # long address is already registered with the system so no need to do anything
579 # long address not registered so add it for short address lookup
580 zigbeeUnregisteredAddressesMutex.acquire()
581 zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
582 zigbeeUnregisteredAddressesMutex.release()
587 def processUdpEnrollmentResponse(parsedData, addr):
589 global zigbeeLongShortAddr
590 global zigbeeLongShortAddrMutex
591 global zigeeBindRequestMutex
592 global zigeeBindRequest
593 global zigbeeConnectionMutex
594 global zigbeeConnection
597 # get the short address for this device long address if possible
598 zigbeeLongShortAddrMutex.acquire()
599 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
600 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
601 zigbeeLongShortAddrMutex.release()
604 # if there is a short address than we can send the message
605 # if there is not one then we cannot since we need both the short and
607 if(shortAddr != None):
609 # get a request number
610 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
615 # send an error message, could not get a sequence number to use at this time
616 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
619 # get the info for sending
620 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
621 destShortAddr = hexStringToZigbeeHexString(shortAddr)
622 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
623 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
624 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
626 # create the payload data
628 payloadData += '\x01'
629 payloadData += chr(seqNumber)
630 payloadData += '\x00'
631 payloadData += '\x00\x00'
633 # create and send binding command
634 zigbeeConnectionMutex.acquire()
635 zigbeeConnection.send('tx_explicit',
637 # frame_id=chr(seqNumber),
638 dest_addr_long=destLongAddr,
639 dest_addr=destShortAddr,
641 dest_endpoint=dstEndpoint,
646 print '> EnrollmentResponse is sent'
647 zigbeeConnectionMutex.release()
651 # send a fail response
652 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
659 def processUdpZclWriteAttributesMessage(parsedData, addr):
661 global zigbeeLongShortAddr
662 global zigbeeLongShortAddrMutex
663 global zigeeBindRequestMutex
664 global zigeeBindRequest
665 global zigbeeConnectionMutex
666 global zigbeeConnection
669 # get the short address for this device long address if possible
670 zigbeeLongShortAddrMutex.acquire()
671 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
672 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
673 zigbeeLongShortAddrMutex.release()
675 # if there is a short address than we can send the message
676 # if there is not one then we cannot since we need both the short and
678 if(shortAddr != None):
679 # get a request number
680 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
685 # send an error message, could not get a sequence number to use at this time
686 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
689 # get the info for sending
690 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
691 destShortAddr = hexStringToZigbeeHexString(shortAddr)
692 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
693 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
694 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
696 # create the payload data
698 payloadData += '\x00'
699 payloadData += chr(seqNumber)
700 payloadData += '\x02'
701 payloadData += '\x10\x00'
702 payloadData += '\xF0'
703 # payloadData += '\xDA\x9A\xD9\x40\x00\xA2\x13\x00'
704 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
706 zigbeeConnectionMutex.acquire()
707 zigbeeConnection.send('tx_explicit',
709 # frame_id=chr(seqNumber),
710 dest_addr_long=destLongAddr,
711 dest_addr=destShortAddr,
713 dest_endpoint=dstEndpoint,
720 print '> WriteAttributesReq is sent : '+str(shortAddr)
721 zigbeeConnectionMutex.release()
725 # send a fail response
726 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
730 def processUdpZclChangeSwitchReqMessage(parsedData, addr):
732 global zigbeeLongShortAddr
733 global zigbeeLongShortAddrMutex
734 global zigeeBindRequestMutex
735 global zigeeBindRequest
736 global zigbeeConnectionMutex
737 global zigbeeConnection
740 # get the short address for this device long address if possible
741 zigbeeLongShortAddrMutex.acquire()
742 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
743 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
744 zigbeeLongShortAddrMutex.release()
747 # if there is a short address than we can send the message
748 # if there is not one then we cannot since we need both the short and
750 if(shortAddr != None):
752 # get a request number
753 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
758 # send an error message, could not get a sequence number to use at this time
759 sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
762 # get the info for sending
763 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
764 destShortAddr = hexStringToZigbeeHexString(shortAddr)
765 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
766 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
767 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
768 value = hexStringToZigbeeHexString(parsedData['value'])
770 # create and send binding command
771 zigbeeConnectionMutex.acquire()
773 zigbeeConnection.send('tx_explicit',
775 # frame_id=chr(seqNumber),
776 dest_addr_long=destLongAddr,
777 dest_addr=destShortAddr,
779 dest_endpoint=dstEndpoint,
782 data='\x01'+chr(seqNumber)+value
785 if parsedData['value']==1:
786 print '> The outlet sensor turned on'
788 print '> The outlet sensor turned off'
790 zigbeeConnectionMutex.release()
794 # send a fail response
795 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
801 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
803 global zigbeeLongShortAddr
804 global zigbeeLongShortAddrMutex
805 global zigeeBindRequestMutex
806 global zigeeBindRequest
807 global zigbeeConnectionMutex
808 global zigbeeConnection
811 # get the short address for this device long address if possible
812 zigbeeLongShortAddrMutex.acquire()
813 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
814 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
815 zigbeeLongShortAddrMutex.release()
818 # if there is a short address than we can send the message
819 # if there is not one then we cannot since we need both the short and
821 if(shortAddr != None):
823 # get a request number
824 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
829 # send an error message, could not get a sequence number to use at this time
830 sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
833 # get the info for sending
834 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
835 destShortAddr = hexStringToZigbeeHexString(shortAddr)
836 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
838 # create and send binding command
839 zigbeeConnectionMutex.acquire()
841 zigbeeConnection.send('tx_explicit',
843 # frame_id=chr(seqNumber),
844 dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
845 dest_addr='\xff\xfe',
847 dest_endpoint=dstEndpoint,
853 print '> BroadcastingRouteRecordReq is sent'
855 zigbeeConnectionMutex.release()
859 # send a fail response
860 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
865 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
867 global zigbeeLongShortAddr
868 global zigbeeLongShortAddrMutex
869 global zigeeBindRequestMutex
870 global zigeeBindRequest
871 global zigbeeConnectionMutex
872 global zigbeeConnection
873 global matchDescriptorReqSingleton
876 # get the short address for this device long address if possible
877 zigbeeLongShortAddrMutex.acquire()
878 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
879 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
880 zigbeeLongShortAddrMutex.release()
883 # if there is a short address than we can send the message
884 # if there is not one then we cannot since we need both the short and
886 if(shortAddr != None):
888 # get a request number
889 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
894 # send an error message, could not get a sequence number to use at this time
895 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
898 # get the info for sending
899 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
900 destShortAddr = hexStringToZigbeeHexString(shortAddr)
901 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
903 # create the payload data
905 payloadData += chr(seqNumber)
906 payloadData += '\x5a'
907 payloadData += '\x00'
909 # create and send binding command
910 zigbeeConnectionMutex.acquire()
911 zigbeeConnection.send('tx_explicit',
913 # frame_id=chr(seqNumber),
914 dest_addr_long=destLongAddr,
915 dest_addr=destShortAddr,
917 dest_endpoint='\x00',
922 print '> ManagementPermitJoiningReq is sent'
925 matchDescriptorReqSingleton= False
926 zigbeeConnectionMutex.release()
930 # send a fail response
931 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
935 def processUdpZclReadAttributesMessage(parsedData, addr):
936 ''' Method handle a ZCL read attribute command
938 parsedData -- Pre-parsed Data that was in the UDP packet.
939 addr -- Address (IP and Port) of the UDP packet origin.
942 global zigbeeLongShortAddr
943 global zigbeeLongShortAddrMutex
944 global zigeeBindRequestMutex
945 global zigeeBindRequest
946 global zigbeeConnectionMutex
947 global zigbeeConnection
951 if(zigbeeAddressAuthorityDict.has_key(addr)):
952 l = zigbeeAddressAuthorityDict[addr]
953 if(parsedData['device_address_long'] not in l):
961 # get the short address for this device long address if possible
962 zigbeeLongShortAddrMutex.acquire()
963 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
964 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
965 zigbeeLongShortAddrMutex.release()
968 # if there is a short address than we can send the message
969 # if there is not one then we cannot since we need both the short and
971 if(shortAddr != None):
973 # get a request number
974 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
979 # send an error message, could not get a sequence number to use at this time
980 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
983 # get the info for sending
984 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
985 destShortAddr = hexStringToZigbeeHexString(shortAddr)
986 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
987 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
988 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
990 # get all the attributes
991 attributeIds = parsedData['attribute_ids'].split(',')
993 # create the payload data
995 payloadData += '\x00'
996 payloadData += chr(seqNumber)
997 payloadData += '\x00'
999 # make all the attributes payloads
1000 for attr in attributeIds:
1002 attr = changeEndian(attr)
1003 payloadData += hexStringToZigbeeHexString(attr)
1005 # create and send binding command
1006 zigbeeConnectionMutex.acquire()
1007 zigbeeConnection.send('tx_explicit',
1009 # frame_id=chr(seqNumber),
1010 dest_addr_long=destLongAddr,
1011 dest_addr=destShortAddr,
1012 src_endpoint='\x00',
1013 dest_endpoint=dstEndpoint,
1018 zigbeeConnectionMutex.release()
1022 # send a fail response
1023 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1026 def processUdpZclConfigureReportingMessage(parsedData, addr):
1027 ''' Method handle a zcl configure reporting message
1029 parsedData -- Pre-parsed Data that was in the UDP packet.
1030 addr -- Address (IP and Port) of the UDP packet origin.
1033 global zigbeeLongShortAddr
1034 global zigbeeLongShortAddrMutex
1035 global zigeeBindRequestMutex
1036 global zigeeBindRequest
1037 global zigbeeConnectionMutex
1038 global zigbeeConnection
1040 if(zigbeeAddressAuthorityDict.has_key(addr)):
1041 l = zigbeeAddressAuthorityDict[addr]
1042 if(parsedData['device_address_long'] not in l):
1050 # get the short address for this device long address if possible
1051 zigbeeLongShortAddrMutex.acquire()
1052 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1053 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1054 zigbeeLongShortAddrMutex.release()
1056 # if there is a short address than we can send the message
1057 # if there is not one then we cannot since we need both the short and
1059 if(shortAddr != None):
1061 # get a request number
1062 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1065 if(seqNumber == -1):
1066 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1069 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1070 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1071 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1072 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1073 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1075 # create the payload data
1077 payloadData += '\x00'
1078 payloadData += chr(seqNumber)
1079 payloadData += '\x06'
1080 payloadData += '\x00'
1081 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1082 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1083 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1084 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1086 if(parsedData.has_key('reportable_change')):
1087 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1090 # create and send binding command
1091 zigbeeConnectionMutex.acquire()
1092 zigbeeConnection.send('tx_explicit',
1094 # frame_id=chr(seqNumber),
1095 dest_addr_long=destLongAddr,
1096 dest_addr=destShortAddr,
1097 src_endpoint='\x00',
1098 dest_endpoint=dstEndpoint,
1103 zigbeeConnectionMutex.release()
1107 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1110 def processUdpPolicySet(parsedData, addr):
1111 ''' Method handle a policy set message
1113 parsedData -- Pre-parsed Data that was in the UDP packet.
1114 addr -- Address (IP and Port) of the UDP packet origin.
1116 print "=================================================================="
1117 print "Policy set: ", parsedData
1118 print 'addr : ', addr
1121 # do nothing if wrong source
1122 if addr == SYSTEM_MASTER_ADDRESS : #or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 :
1123 key = (parsedData['ip_address'], int(parsedData['port']))
1124 if (zigbeeAddressAuthorityDict.has_key(key)):
1125 zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1127 zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1130 def processUdpPolicyClear(parsedData, addr):
1131 ''' Method handle a policy set message
1133 parsedData -- Pre-parsed Data that was in the UDP packet.
1134 addr -- Address (IP and Port) of the UDP packet origin.
1136 print "=================================================================="
1137 print "Clear policy: ", parsedData
1139 # do nothing if wrong source
1140 if addr == SYSTEM_MASTER_ADDRESS : #or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1141 zigbeeAddressAuthorityDict.clear()
1148 def processZigbeeATCommandMessage(parsedData):
1149 ''' Method to process an AT zigbee message
1151 parsedData -- Pre-parsed (into a dict) data from message.
1153 global ZIGBEE_DEVICE_ADDRESS
1154 global didGetLocalRadioHighAddress
1155 global didGetLocalRadioLowAddress
1157 # command response for the high bytes of the local device long address
1158 if(parsedData['command'] == 'SH'):
1159 # convert the parameter to a string value (human readable)
1161 for e in parsedData['parameter']:
1162 value += "{0:02x}".format(ord(e))
1164 # set the correct portion of the address
1165 ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1167 #signal that we got this part of the address
1168 didGetLocalRadioHighAddress = True
1170 # command response for the low bytes of the local device long address
1171 elif(parsedData['command'] == 'SL'):
1172 # convert the parameter to a string value (human readable)
1174 for e in parsedData['parameter']:
1175 value += "{0:02x}".format(ord(e))
1177 # set the correct portion of the address
1178 ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1180 #signal that we got this part of the address
1181 didGetLocalRadioLowAddress = True
1183 def processZigbeeRxExplicitCommandMessage(parsedData):
1184 ''' Method to process a rx-explicit zigbee message
1186 parsedData -- Pre-parsed (into a dict) data from message.
1188 global zigeeBindRequestMutex
1189 global zigeeBindRequest
1190 global zigbeeConnectionMutex
1191 global zigbeeConnection
1192 global ManagementPermitJoiningReqSuccess
1194 # get the long and short addresses from the message payload since we can
1195 # use these to update the short addresses since this short address is fresh
1196 longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1197 shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1199 # check if this short address is for a device that has yet to be
1201 zigbeeUnregisteredAddressesMutex.acquire()
1202 if(longAddr in zigbeeUnregisteredAddresses):
1203 zigbeeUnregisteredAddresses.remove(longAddr)
1204 zigbeeUnregisteredAddressesMutex.release()
1206 # update/ or insert the short address
1207 zigbeeLongShortAddrMutex.acquire()
1208 zigbeeLongShortAddr[longAddr] = shortAddr
1209 zigbeeLongShortAddrMutex.release()
1211 global matchDescriptorReqSingleton
1212 global deviceAnnouncementSingleton
1213 global seqNumberForNotification
1215 # if this is a ZDO message/response
1216 if(parsedData['profile'] == '\x00\x00'):
1219 # if this is a Match Descriptor Request so we need to answer.
1220 if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1221 zigbeeConnectionMutex.acquire()
1222 zigbeeConnection.send('tx_explicit',
1224 # frame_id=chr(seqNumber),
1225 dest_addr_long=parsedData['source_addr_long'],
1226 dest_addr=parsedData['source_addr'],
1227 src_endpoint='\x00',
1228 dest_endpoint='\x00',
1231 data=parsedData['rf_data']
1234 zigbeeConnection.send('tx_explicit',
1236 # frame_id=chr(seqNumber),
1237 dest_addr_long=parsedData['source_addr_long'],
1238 dest_addr=parsedData['source_addr'],
1239 src_endpoint='\x00',
1240 dest_endpoint='\x00',
1243 data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1247 print '[ 0x0006 ] Match Descriptor Request - answered'
1248 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1249 zigbeeConnectionMutex.release()
1252 # if this is a device announcement so we can get some useful data from it
1253 elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1255 # pick out the correct parts of the payload
1256 longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1257 shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1259 # change the endian of the address
1260 longAddr = changeEndian(longAddr)
1261 shortAddr = changeEndian(shortAddr)
1263 # update the table with the new information
1264 zigbeeLongShortAddrMutex.acquire()
1265 zigbeeLongShortAddr[longAddr] = shortAddr
1266 zigbeeLongShortAddrMutex.release()
1268 # check if this short address is for a device that has yet to be
1270 zigbeeUnregisteredAddressesMutex.acquire()
1271 if(longAddr in zigbeeUnregisteredAddresses):
1272 zigbeeUnregisteredAddresses.remove(longAddr)
1273 zigbeeUnregisteredAddressesMutex.release()
1277 zigbeeConnectionMutex.acquire()
1278 zigbeeConnection.send('tx_explicit',
1280 # frame_id=chr(seqNumber),
1281 dest_addr_long=parsedData['source_addr_long'],
1282 dest_addr=parsedData['source_addr'],
1283 src_endpoint='\x00',
1284 dest_endpoint='\x00',
1287 data=parsedData['rf_data']
1290 print '[ 0x0013 ] device announcement - answered'
1291 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1292 deviceAnnouncementSingleton = False
1293 zigbeeConnectionMutex.release()
1296 # if this is a response to a zdo bind_req message
1297 elif(parsedData['cluster'] == '\x80\x21'):
1299 # get the status and sequence number from the message
1300 seqNumber = parsedData['rf_data'][0]
1301 statusCode = parsedData['rf_data'][1]
1303 # get the bind tuple information
1304 # for this specific bind request
1306 zigeeBindRequestMutex.acquire()
1307 if(zigeeBindRequest.has_key(ord(seqNumber))):
1308 tup = zigeeBindRequest[ord(seqNumber)]
1309 zigeeBindRequestMutex.release()
1312 # cant really do anything in this case...
1313 # don't have any information on who the data is for
1316 # successful binding
1317 if(ord(statusCode) == 0):
1319 # add a callback for this specific device and cluster
1320 # to the HA callback dict
1321 zibeeHACallbackMutex.acquire();
1322 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1323 if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1324 zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1326 zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1327 zibeeHACallbackMutex.release()
1329 # send success message
1330 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1333 elif (ord(statusCode) == 170):
1334 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1337 elif (ord(statusCode) == 174):
1338 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1340 # Other issue, dont have code for
1342 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1344 # if this is a response to a short address query
1345 elif(parsedData['cluster'] == '\x80\x00'):
1348 statusCode = parsedData['rf_data'][0]
1350 # does not matter if this is not a success, we can try again later
1351 if(statusCode != '\x00'):
1352 # status code was not success so do not do anything
1355 # get the short and long address information
1356 longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1357 shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1359 # remove device from list of unregistered devices if it is in it
1360 zigbeeUnregisteredAddressesMutex.acquire()
1361 if(longAddr in zigbeeUnregisteredAddresses):
1362 zigbeeUnregisteredAddresses.remove(longAddr)
1363 zigbeeUnregisteredAddressesMutex.release()
1365 # update/insert the short address
1366 zigbeeLongShortAddrMutex.acquire()
1367 zigbeeLongShortAddr[longAddr] = shortAddr
1368 zigbeeLongShortAddrMutex.release()
1371 elif(parsedData['cluster'] == '\x80\x06'):
1373 print '[ 0x8006 ] get Match Descriptor Response'
1374 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1377 elif(parsedData['cluster'] == '\x80\x36'):
1379 print '[ 0x8036 ] get Management Permit Joining Response'
1380 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1382 ManagementPermitJoiningReqSuccess = True
1387 print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1388 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1391 # if this is a home automation zcl message/response
1392 elif (parsedData['profile'] == '\x01\x04'):
1394 # get the zcl message header
1395 zclFrameControl = parsedData['rf_data'][0]
1396 zclSeqNumber = parsedData['rf_data'][1]
1397 zclCommand = parsedData['rf_data'][2]
1398 zclStatus = parsedData['rf_data'][3]
1401 if(zclCommand == '\x00'):
1403 print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1405 # find who to send response
1407 zigbeeSeqNumberToClientMutex.acquire()
1409 if(longAddr in seqNumberForNotification):
1411 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1412 tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1413 #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1414 zigbeeSeqNumberToClientMutex.release()
1416 # no one to send the response to so just move on
1418 # cant really do anything here
1420 # create the response message
1422 message = "type : zcl_zone_status_change_notification\n"
1423 message += "packet_id: " + packetId + "\n"
1424 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1425 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1426 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1427 message += "attributes: success"
1430 sendSoceket.sendto(message,tup[0])
1431 print(">port : ", tup[0][1])
1435 # this is a zcl read attribute response
1436 elif(zclCommand == '\x01'):
1438 # get the zcl payload
1439 zclPayload = parsedData['rf_data'][3:]
1440 attibuteResponseList = []
1442 # get the data for each data
1443 while(len(zclPayload) > 0):
1444 attributeId = zclPayload[0:2]
1445 attributeStatus = zclPayload[2]
1446 zclPayload = zclPayload[3:]
1448 if(ord(attributeStatus) != 0):
1449 # if attribute is not supported then it has no data
1450 # package the data and add it to the list
1451 attibuteResponseList.append((attributeId,"not_supported"))
1454 # get the data type and data length of the attributre
1455 attributeType = zclPayload[0]
1456 dataLength = zclDataTypeToBytes(zclPayload)
1458 # consume zcl payload data
1459 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1460 zclPayload = zclPayload[2:]
1461 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1462 zclPayload = zclPayload[3:]
1464 zclPayload = zclPayload[1:]
1466 # package the data and add it to the list
1467 newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1468 attibuteResponseList.append(newData)
1470 # consume the data size of the payload
1471 zclPayload = zclPayload[dataLength:]
1473 # find who to send response to
1475 zigbeeSeqNumberToClientMutex.acquire()
1476 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1477 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1478 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1479 zigbeeSeqNumberToClientMutex.release()
1481 # no one to send the response to so just move on
1483 # cant really do anything here
1486 # create the response message
1488 message = "type : zcl_read_attributes_response \n"
1489 message += "packet_id: " + packetId + "\n"
1490 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1491 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1492 message += "attributes: "
1494 # create the message for each attribute
1495 for t in attibuteResponseList:
1496 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1497 if(t[1] == "success"):
1498 attrIdStr = "%0.4x" % attrId
1499 attrIdStr = changeEndian(attrIdStr)
1501 message += attrIdStr
1503 message += "success"
1505 message += "%0.2x" % ord(t[2])
1510 dat += "%0.2x" % ord(c)
1511 dat = changeEndian(dat)
1515 attrIdStr = "%0.4x" % attrId
1516 attrIdStr = changeEndian(attrIdStr)
1518 message += attrIdStr
1520 message += "not_supported"
1523 message = message[0:len(message) - 1]
1526 sendSoceket.sendto(message,tup[0])
1532 # this is a zcl write attribute response
1533 elif(zclCommand == '\x04'):
1535 # get the zcl payload
1536 zclPayload = parsedData['rf_data'][3]
1537 # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1538 if(zclStatus == '\x70' or zclPayload == '\x00'):
1540 # find who to send response to
1542 zigbeeSeqNumberToClientMutex.acquire()
1543 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1544 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1545 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1546 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1547 zigbeeSeqNumberToClientMutex.release()
1548 # no one to send the response to so just move on
1550 # cant really do anything here
1553 # create the response message
1555 message = "type : zcl_write_attributes_response\n"
1556 message += "packet_id: " + packetId + "\n"
1557 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1558 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1559 message += "attributes: success"
1562 sendSoceket.sendto(message,tup[0])
1564 print '[ 0x0500 ] get Write Attribute Response success'
1565 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1569 print '[ 0x0500 ] get Write Attribute Response'
1570 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1574 # this is a zcl configure attribute response
1575 elif(zclCommand == '\x07'):
1577 # find who to send response to
1579 zigbeeSeqNumberToClientMutex.acquire()
1580 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1581 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1582 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1583 zigbeeSeqNumberToClientMutex.release()
1585 # no one to send the response to so just move on
1587 # cant really do anything here
1591 zclPayload = parsedData['rf_data'][3:]
1593 # construct the message
1595 message = "type : zcl_configure_reporting_response \n"
1596 message += "packet_id: " + packetId + "\n"
1597 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1598 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1599 message += "attributes: "
1601 if(len(zclPayload) == 1):
1602 # if all the configurations are a success then only send back a success
1603 # based on zigbee specs
1604 message += "all_success \n";
1605 sendSoceket.sendto(message,tup[0])
1608 attibuteResponseList = []
1610 # get each attributes data
1611 while(len(zclPayload) > 0):
1612 attributeStatus = zclPayload[0]
1613 attributeDirection = zclPayload[1]
1614 attributeId = zclPayload[2:4]
1615 zclPayload = zclPayload[4:]
1617 newData = (attributeStatus,attributeDirection, attributeId)
1618 attibuteResponseList.append(newData)
1620 # package each attribute
1621 for t in attibuteResponseList:
1622 attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1623 attrIdStr = "%0.4x" % attrId
1624 attrIdStr = changeEndian(attrIdStr)
1626 message += attrIdStr
1629 message += "success"
1636 message += "reported"
1638 message += "received"
1641 message = message[0:len(message) - 1]
1643 sendSoceket.sendto(message,tup[0])
1645 # this is a zcl report attribute message
1646 elif(zclCommand == '\x0a'):
1647 print "get Report attribute "
1648 # get teh zcl payload
1649 zclPayload = parsedData['rf_data'][3:]
1650 attibuteResponseList = []
1652 # extract the attribute data
1653 while(len(zclPayload) > 0):
1654 attributeId = zclPayload[0:2]
1655 zclPayload = zclPayload[2:]
1656 attributeType = zclPayload[0]
1657 dataLength = zclDataTypeToBytes(zclPayload)
1659 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1660 zclPayload = zclPayload[2:]
1661 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1662 zclPayload = zclPayload[3:]
1664 zclPayload = zclPayload[1:]
1666 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1667 attibuteResponseList.append(newData)
1668 zclPayload = zclPayload[dataLength:]
1671 # get callback clients to respond to
1672 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1674 zibeeHACallbackMutex.acquire()
1675 if(zibeeHACallback.has_key(callbackIndex)):
1676 retAddr = zibeeHACallback[callbackIndex]
1677 zibeeHACallbackMutex.release()
1679 # no one to respond to so do nothing here
1680 if(retAddr == None):
1683 # construct the message
1684 message = "type : zcl_report_attributes \n"
1685 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1686 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1687 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1688 message += "attributes: "
1690 # package the attributes
1691 for t in attibuteResponseList:
1692 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1693 attrIdStr = "%0.4x" % attrId
1694 attrIdStr = changeEndian(attrIdStr)
1696 message += attrIdStr
1698 message += "%0.2x" % ord(t[1])
1703 dat += "%0.2x" % ord(c)
1704 dat = changeEndian(dat)
1708 message = message[0:len(message) - 1]
1710 print "Sending", message
1712 # send to all client that want this callback
1714 sendSoceket.sendto(message,ra)
1716 # -----------------------------------------------------------------------------
1717 # Communication Callback/Parse Methods
1718 # -----------------------------------------------------------------------------
1719 def handleNewZigbeeMessage(parsedData):
1720 ''' Method to process a zigbee message from the local radio.
1722 parsedData -- Pre-parsed (into a dict) data from message.
1724 #print "=================================================================="
1726 #print "New Zigbee Message"
1727 #printMessageData(parsedData)
1729 # dispatch to the correct zigbee handler
1730 if (parsedData['id'] == 'at_response'):
1731 processZigbeeATCommandMessage(parsedData)
1733 elif (parsedData['id'] == 'rx_explicit'):
1734 processZigbeeRxExplicitCommandMessage(parsedData)
1737 print "Unknown API format"
1739 #print "=================================================================="
1743 def handleNewUdpPacket(data, addr):
1744 ''' Method to parse and handle an incoming UDP packet.
1746 data -- Data that was in the UDP packet.
1747 addr -- Address (IP and Port) of the UDP packet origin.
1749 global ManagementPermitJoiningReqSuccess
1751 #print "=================================================================="
1753 #print "Got New UDP packet..."
1757 # data comes in as 'key: value\n key: value...' string and so needs to be
1758 # parsed into a dict
1761 # 1 key, value pair per line
1762 for line in data.split('\n'):
1764 # key and values are split based on a ':'
1765 fields = line.split(':')
1767 # make sure properly formated otherwise just ignore it
1768 if len(fields) == 2:
1770 # do strips to remove any white spacing that may have resulted
1771 # from improper packing on the sender side
1772 parsedData[fields[0].strip()] = fields[1].strip()
1775 # wrap in try statement just in case there is an improperly formated packet we
1778 # dispatch to the correct process method
1779 if(parsedData["type"] == "zdo_bind_request"):
1780 processUdpZdoBindReqMessage(parsedData, addr)
1781 elif(parsedData["type"] == "zdo_unbind_request"):
1782 processUdpZdoUnBindReqMessage(parsedData, addr)
1783 elif(parsedData["type"] == "send_address"):
1784 processUdpSendAddressMessage(parsedData, addr)
1785 elif(parsedData["type"] == "zcl_read_attributes"):
1786 processUdpZclReadAttributesMessage(parsedData, addr)
1787 elif(parsedData["type"] == "zcl_configure_reporting"):
1788 processUdpZclConfigureReportingMessage(parsedData, addr)
1789 elif(parsedData["type"] == "policy_set"):
1790 processUdpPolicySet(parsedData, addr)
1791 elif(parsedData["type"] == "policy_clear"):
1792 processUdpPolicyClear(parsedData, addr)
1793 elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo
1794 processUdpManagementPermitJoiningReqMessage(parsedData, addr)
1795 elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo
1796 processUdpZclWriteAttributesMessage(parsedData, addr)
1797 elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo
1798 processUdpEnrollmentResponse(parsedData, addr)
1799 elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo
1800 processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
1801 elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo
1802 processUdpZclChangeSwitchReqMessage(parsedData, addr)
1804 #print "unknown Packet: " + parsedData["type"]
1807 # if we ever get here then something went wrong and so just ignore this
1808 # packet and try again later
1809 print "I didn't expect this error:", sys.exc_info()[0]
1810 traceback.print_exc()
1812 #print "=================================================================="
1815 # -----------------------------------------------------------------------------
1816 # Main Running Methods
1817 # -----------------------------------------------------------------------------
1820 '''Main function used for starting the application as the main driver'''
1822 global ZIGBEE_SERIAL_PORT
1823 global ZIGBEE_SERIAL_BAUD
1824 global UDP_RECEIVE_PORT
1825 global zigbeeConnection
1829 parseCommandLineArgs(sys.argv[1:])
1831 # create serial object used for communication to the zigbee radio
1832 sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
1834 # create a zigbee object that handles all zigbee communication
1835 # we use this to do all communication to and from the radio
1836 # when data comes from the radio it will get a bit of unpacking
1837 # and then a call to the callback specified will be done with the
1839 zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
1841 # get the long address of our local radio before we start doing anything
1842 getConnectedRadioLongAddress();
1844 # setup incoming UDP socket and bind it to self and specified UDP port
1845 # sending socket does not need to be bound to anything
1846 receiveSoceket.bind(('127.0.0.1', UDP_RECEIVE_PORT))
1848 # create the thread that does short address lookups
1849 addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
1850 addressUpdateWorkerThread.start()
1855 #print "=================================================================="
1858 #print "=================================================================="
1860 # wait for an incoming UDP packet
1861 # this is a blocking call
1862 data, addr = receiveSoceket.recvfrom(4096)
1864 # handle the UDP packet appropriately
1865 handleNewUdpPacket(data, addr)
1867 except KeyboardInterrupt:
1868 # use the keyboard interupt to catch a ctrl-c and kill the application
1872 # something went really wrong and so exit with error message
1873 traceback.print_exc()
1875 # signal all threads to exit
1878 # wait for threads to finish before closing of the resources
1879 addressUpdateWorkerThread.join()
1882 # make sure to close all the connections
1883 zigbeeConnection.halt()
1884 receiveSoceket.close()
1887 if __name__ == "__main__":
1888 # call main function since this is being run as the start