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 = ("192.168.2.108", 12345) # ip address and portof the system master node
21 # time for messages to wait for a response before the system clears away that
23 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5
25 #ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH" # USB-Serial port of local radio
26 ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
27 ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port
29 # address of our local zigbee radio
30 #ZIGBEE_DEVICE_ADDRESS = "0013a20040d99cb4"
31 ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx"
33 # -----------------------------------------------------------------------------
34 # Global Variables and Objects
35 # -----------------------------------------------------------------------------
37 # signals to see if a request needs to be made
38 didGetLocalRadioHighAddress = False;
39 didGetLocalRadioLowAddress = False;
41 # zigbee communications object and its mutex
42 zigbeeConnection = None
43 zigbeeConnectionMutex = Lock()
45 #singleton mabe by changwoo
46 matchDescriptorReqSingleton = True
47 deviceAnnouncementSingleton = True
48 ManagementPermitJoiningReqSuccess = False
50 # zigbee mapping from long to short object dict
51 zigbeeLongShortAddr = dict()
52 zigbeeLongShortAddrMutex = Lock()
54 # zigbee mapping from a sequence number to a client
55 # for correct response handling
56 zigbeeSeqNumberToClient = dict()
57 zigbeeSeqNumberToClientMutex = Lock()
59 zigeeBindRequest = dict()
60 zigeeBindRequestMutex = Lock()
62 # Keeps record of where to send callbacks to when an HA message is received
63 zibeeHACallback = dict()
64 zibeeHACallbackMutex = Lock()
67 # Keeps a record of device addresses whose short addresses have not been
69 zigbeeUnregisteredAddresses = []
70 zigbeeUnregisteredAddressesMutex = Lock()
72 # used to signal all threads to end
76 # 2 sockets, one for sending (not bound to a port manually)
77 # and one for receiving, known port binding by application
79 sendSoceket = socket(AF_INET, SOCK_DGRAM)
80 receiveSoceket = socket(AF_INET, SOCK_DGRAM)
82 # zigbee address authority list
83 zigbeeAddressAuthorityDict = dict()
86 seqNumberForNotification = dict()
88 # -----------------------------------------------------------------------------
90 # -----------------------------------------------------------------------------
91 def reverseShortAddress(shortAddr):
92 result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
95 def parseCommandLineArgs(argv):
96 global ZIGBEE_SERIAL_PORT
97 global ZIGBEE_SERIAL_BAUD
99 opts, args = getopt.getopt(
100 argv, "hp:b:u:", ["port=", "baud=", "udpport="])
102 except getopt.GetoptError:
103 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
106 for opt, arg in opts:
108 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
111 elif opt in ("-p", "--port"):
112 ZIGBEE_SERIAL_PORT = arg
114 elif opt in ("-b", "--baud"):
116 ZIGBEE_SERIAL_BAUD = int(arg)
118 print "Buad rate must be an integer"
123 # Convenience (Stateless)
126 def hexListToChar(hexList):
127 ''' Method to convert a list/string of characters into their corresponding values
129 hexList -- list or string of hex characters
133 retString += chr(int(h, 16))
136 def splitByN(seq, n):
137 ''' Method to split a string into groups of n characters
142 return [seq[i:i+n] for i in range(0, len(seq), n)]
144 def changeEndian(hexString):
145 ''' Method to change endian of a hex string
147 hexList -- string of hex characters
149 split = splitByN(hexString, 2) # get each byte
150 split.reverse(); # reverse ordering of the bytes
158 def printMessageData(data):
159 ''' Method to print a zigbee message to the console
161 data -- pre-parsed zigbee message
166 print "{0:02x}".format(ord(e)),
168 print "({})".format(data[d]),
171 def hexStringToZigbeeHexString(hexString):
172 ''' Method to change a hex string to a string of characters with the hex values
174 hexList -- string of hex characters
176 return hexListToChar(splitByN(hexString, 2))
178 def zigbeeHexStringToHexString(zigbeeHexString):
179 ''' Method to change string of characters with the hex values to a hex string
181 hexList -- string of characters with hex values
185 for e in zigbeeHexString:
186 retString += "{0:02x}".format(ord(e))
189 def zclDataTypeToBytes(zclPayload):
190 ''' Method to determine data length of a zcl attribute
192 zclPayload -- ZCL payload data, must have dataType as first byte
194 attrType = ord(zclPayload[0])
196 if(attrType == 0x00):
198 elif (attrType == 0x08):
200 elif (attrType == 0x09):
202 elif (attrType == 0x0a):
204 elif (attrType == 0x0b):
206 elif (attrType == 0x0c):
208 elif (attrType == 0x0d):
210 elif (attrType == 0x0e):
212 elif (attrType == 0x0f):
214 elif (attrType == 0x10):
216 elif (attrType == 0x18):
218 elif (attrType == 0x19):
220 elif (attrType == 0x1a):
222 elif (attrType == 0x1b):
224 elif (attrType == 0x1c):
226 elif (attrType == 0x1d):
228 elif (attrType == 0x1e):
230 elif (attrType == 0x1f):
232 elif (attrType == 0x20):
234 elif (attrType == 0x21):
236 elif (attrType == 0x22):
238 elif (attrType == 0x23):
240 elif (attrType == 0x24):
242 elif (attrType == 0x25):
244 elif (attrType == 0x26):
246 elif (attrType == 0x27):
248 elif (attrType == 0x28):
250 elif (attrType == 0x29):
252 elif (attrType == 0x2a):
254 elif (attrType == 0x2b):
256 elif (attrType == 0x2c):
258 elif (attrType == 0x2d):
260 elif (attrType == 0x2e):
262 elif (attrType == 0x2f):
264 elif (attrType == 0x30):
266 elif (attrType == 0x31):
268 elif (attrType == 0x38):
270 elif (attrType == 0x39):
272 elif (attrType == 0x3a):
274 elif (attrType == 0x41):
275 return ord(zclPayload[1])
276 elif (attrType == 0x42):
277 return ord(zclPayload[1])
278 elif (attrType == 0x43):
279 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
280 elif (attrType == 0x44):
281 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
282 elif (attrType == 0xe0):
284 elif (attrType == 0xe1):
286 elif (attrType == 0xe2):
288 elif (attrType == 0xe8):
290 elif (attrType == 0xe9):
292 elif (attrType == 0xea):
294 elif (attrType == 0xf0):
296 elif (attrType == 0xf1):
298 elif (attrType == 0xff):
305 def createSequenceNumberForClient(addr, packetId):
306 ''' Method to get and store a sequence number with a specific client
307 for a specific message.
309 addr -- UDP address of the client to associate with the seq. number
310 packetId -- packet id from the UDP packet
312 # keep trying to find a number to return
315 # get the current time
316 epoch_time = int(time.time())
318 # get the current list of used numbers
319 zigbeeSeqNumberToClientMutex.acquire()
320 keysList = zigbeeSeqNumberToClient.keys()
321 zigbeeSeqNumberToClientMutex.release()
323 # if all the numbers are already used
324 if(len(keysList) == 256):
326 # get a list of all the items
327 zigbeeSeqNumberToClientMutex.acquire()
328 itemsList = zigbeeSeqNumberToClient.items()
329 zigbeeSeqNumberToClientMutex.release()
331 # search for a number that is old enough to get rid of otherwise use -1
333 for item in itemsList:
334 if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC):
339 # replace the record with new data if we found one to replace
340 zigbeeSeqNumberToClientMutex.acquire()
341 zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId)
342 zigbeeSeqNumberToClientMutex.release()
347 # not all numbers used yet so pick one randomly
348 randNum = random.randrange(0,256)
350 # check if we are using the number yet
351 if(randNum not in keysList):
353 # we are not so insert to keep track who this number is for and return it
354 zigbeeSeqNumberToClientMutex.acquire()
355 zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId)
356 zigbeeSeqNumberToClientMutex.release()
359 def getConnectedRadioLongAddress():
360 """ Method to make sure we get the MAC address of our local radio"""
361 global zigbeeConnection
364 # keep looping until we get both the MSBs and the LSBs
365 while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)):
368 zigbeeConnection.send('at', command="SH")
369 zigbeeConnection.send('at', command="SL")
371 # sleep for a bit to give the radio time to respond before we check again
374 def addressUpdateWorkerMethod():
375 ''' Method to keep refreshing the short addresses of the known zigbee devices'''
377 global zigbeeLongShortAddr
378 global zigbeeLongShortAddrMutex
379 global zigbeeUnregisteredAddresses
380 global zigbeeUnregisteredAddressesMutex
381 global zigbeeConnectionMutex
382 global zigbeeConnection
384 # keep looping until signaled to quit
385 while(not doEndFlag):
389 # add unregistered (short addresses unknown) devices so
390 # that we can look them up
391 zigbeeUnregisteredAddressesMutex.acquire()
392 addrList.extend(zigbeeUnregisteredAddresses)
393 zigbeeUnregisteredAddressesMutex.release()
395 # add the devices that we have short addresses for so we can
396 # get their most recent short addresses
397 zigbeeLongShortAddrMutex.acquire()
398 addrList.extend(zigbeeLongShortAddr.keys())
399 zigbeeLongShortAddrMutex.release()
401 # Loop through all the addresses and send messages for each address
404 # create payload for a query on the network for a short address
406 payload += hexStringToZigbeeHexString(changeEndian(ad))
409 # create and send binding command
410 zigbeeConnectionMutex.acquire()
412 zigbeeConnection.send('tx_explicit',
414 dest_addr_long=hexStringToZigbeeHexString(ad),
415 dest_addr='\xff\xfd',
417 dest_endpoint='\x00',
422 zigbeeConnectionMutex.release()
431 def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
432 ''' Method to send a success or fail back to a client.
434 addr -- UDP address to send packet to
435 packetTypeStr -- name of this specific packet
436 packetIdStr -- packet id to send
437 sucOrFail -- whether this is a success or fail message (True = success)
438 reason -- reason of failure (if needed, default is None)
444 # construct the message
445 message = "type: " + packetTypeStr.strip() + "\n"
446 message += "packet_id: " + packetIdStr + "\n"
449 message += "response: success \n"
451 message += "response: fail \n"
452 message += "reason: " + reason + "\n"
454 # send message in a UDP packet
455 sendSoceket.sendto(message,addr)
457 def processUdpZdoBindReqMessage(parsedData, addr):
461 if(zigbeeAddressAuthorityDict.has_key(addr)):
462 l = zigbeeAddressAuthorityDict[addr]
463 if(parsedData['device_address_long'] not in l):
468 # get the short address for this device long address if possible
469 zigbeeLongShortAddrMutex.acquire()
470 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
471 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
472 zigbeeLongShortAddrMutex.release()
474 # if there is a short address than we can send the message
475 # if there is not one then we cannot since we need both the short and
477 if(shortAddr != None):
479 # get a request number
480 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
485 # send an error message, could not get a sequence number to use at this time
486 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
489 # a bind request was made so must store and wait for response
490 # before we setup callbacks, so keep just the data we need to create the callback
491 zigeeBindRequestMutex.acquire()
492 zigeeBindRequest[seqNumber] = (parsedData['device_address_long'],
493 parsedData['cluster_id'],
494 parsedData['packet_id'],
496 zigeeBindRequestMutex.release()
498 # construct the short and long addresses of the message for sending
499 # make sure they are in the correct format
500 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
501 destShortAddr = hexStringToZigbeeHexString(shortAddr)
503 # create the payload data
505 payloadData += chr(seqNumber)
506 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
507 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
508 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id']))
509 payloadData += '\x03'
510 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
511 payloadData += '\x00'
513 # create and send binding command
514 zigbeeConnectionMutex.acquire()
515 zigbeeConnection.send('tx_explicit',
517 # frame_id=chr(seqNumber),
518 dest_addr_long=destLongAddr,
519 dest_addr=destShortAddr,
521 dest_endpoint='\x00',
526 zigbeeConnectionMutex.release()
530 # send a failure packet since there is no short address available
531 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
534 def processUdpZdoUnBindReqMessage(parsedData, addr):
535 zibeeHACallbackMutex.acquire();
536 if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
537 zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
538 zibeeHACallbackMutex.release()
539 sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
543 def processUdpSendAddressMessage(parsedData, addr):
544 ''' Method handle a send address command
546 parsedData -- Pre-parsed Data that was in the UDP packet.
547 addr -- Address (IP and Port) of the UDP packet origin.
549 global zigbeeLongShortAddr
550 global zigbeeLongShortAddrMutex
551 global zigbeeUnregisteredAddresses
552 global zigbeeUnregisteredAddressesMutex
555 print "process send address"
558 # construct success message
559 message = "type: send_address_response\n"
560 message += "packet_id: " + parsedData['packet_id'] + "\n"
561 message += "response: success\n"
563 # tell client that we got their request
564 sendSoceket.sendto(message,addr)
565 print "responding", message
568 zigbeeLongShortAddrMutex.acquire()
569 doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
570 zigbeeLongShortAddrMutex.release()
573 # long address is already registered with the system so no need to do anything
576 # long address not registered so add it for short address lookup
577 zigbeeUnregisteredAddressesMutex.acquire()
578 zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
579 zigbeeUnregisteredAddressesMutex.release()
582 def processUdpEnrollmentResponse(parsedData, addr):
584 global zigbeeLongShortAddr
585 global zigbeeLongShortAddrMutex
586 global zigeeBindRequestMutex
587 global zigeeBindRequest
588 global zigbeeConnectionMutex
589 global zigbeeConnection
592 # get the short address for this device long address if possible
593 zigbeeLongShortAddrMutex.acquire()
594 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
595 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
596 zigbeeLongShortAddrMutex.release()
599 # if there is a short address than we can send the message
600 # if there is not one then we cannot since we need both the short and
602 if(shortAddr != None):
604 # get a request number
605 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
610 # send an error message, could not get a sequence number to use at this time
611 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
614 # get the info for sending
615 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
616 destShortAddr = hexStringToZigbeeHexString(shortAddr)
617 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
618 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
619 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
621 # create the payload data
623 payloadData += '\x01'
624 payloadData += chr(seqNumber)
625 payloadData += '\x00'
626 payloadData += '\x00\x00'
628 # create and send binding command
629 zigbeeConnectionMutex.acquire()
630 zigbeeConnection.send('tx_explicit',
632 # frame_id=chr(seqNumber),
633 dest_addr_long=destLongAddr,
634 dest_addr=destShortAddr,
636 dest_endpoint=dstEndpoint,
641 print '> EnrollmentResponse is sent'
642 zigbeeConnectionMutex.release()
646 # send a fail response
647 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
654 def processUdpZclWriteAttributesMessage(parsedData, addr):
656 global zigbeeLongShortAddr
657 global zigbeeLongShortAddrMutex
658 global zigeeBindRequestMutex
659 global zigeeBindRequest
660 global zigbeeConnectionMutex
661 global zigbeeConnection
664 # get the short address for this device long address if possible
665 zigbeeLongShortAddrMutex.acquire()
666 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
667 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
668 zigbeeLongShortAddrMutex.release()
670 # if there is a short address than we can send the message
671 # if there is not one then we cannot since we need both the short and
673 if(shortAddr != None):
674 # get a request number
675 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
680 # send an error message, could not get a sequence number to use at this time
681 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
684 # get the info for sending
685 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
686 destShortAddr = hexStringToZigbeeHexString(shortAddr)
687 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
688 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
689 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
691 # create the payload data
693 payloadData += '\x00'
694 payloadData += chr(seqNumber)
695 payloadData += '\x02'
696 payloadData += '\x10\x00'
697 payloadData += '\xF0'
698 # payloadData += '\xDA\x9A\xD9\x40\x00\xA2\x13\x00'
699 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
701 zigbeeConnectionMutex.acquire()
702 zigbeeConnection.send('tx_explicit',
704 # frame_id=chr(seqNumber),
705 dest_addr_long=destLongAddr,
706 dest_addr=destShortAddr,
708 dest_endpoint=dstEndpoint,
715 print '> WriteAttributesReq is sent : '+str(shortAddr)
716 zigbeeConnectionMutex.release()
720 # send a fail response
721 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
725 def processUdpZclChangeSwitchReqMessage(parsedData, addr):
727 global zigbeeLongShortAddr
728 global zigbeeLongShortAddrMutex
729 global zigeeBindRequestMutex
730 global zigeeBindRequest
731 global zigbeeConnectionMutex
732 global zigbeeConnection
735 # get the short address for this device long address if possible
736 zigbeeLongShortAddrMutex.acquire()
737 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
738 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
739 zigbeeLongShortAddrMutex.release()
742 # if there is a short address than we can send the message
743 # if there is not one then we cannot since we need both the short and
745 if(shortAddr != None):
747 # get a request number
748 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
753 # send an error message, could not get a sequence number to use at this time
754 sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
757 # get the info for sending
758 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
759 destShortAddr = hexStringToZigbeeHexString(shortAddr)
760 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
761 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
762 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
763 value = hexStringToZigbeeHexString(parsedData['value'])
765 # create and send binding command
766 zigbeeConnectionMutex.acquire()
768 zigbeeConnection.send('tx_explicit',
770 # frame_id=chr(seqNumber),
771 dest_addr_long=destLongAddr,
772 dest_addr=destShortAddr,
774 dest_endpoint=dstEndpoint,
777 data='\x01'+chr(seqNumber)+value
780 if parsedData['value']==1:
781 print '> The outlet sensor turned on'
783 print '> The outlet sensor turned off'
785 zigbeeConnectionMutex.release()
789 # send a fail response
790 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
796 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
798 global zigbeeLongShortAddr
799 global zigbeeLongShortAddrMutex
800 global zigeeBindRequestMutex
801 global zigeeBindRequest
802 global zigbeeConnectionMutex
803 global zigbeeConnection
806 # get the short address for this device long address if possible
807 zigbeeLongShortAddrMutex.acquire()
808 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
809 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
810 zigbeeLongShortAddrMutex.release()
813 # if there is a short address than we can send the message
814 # if there is not one then we cannot since we need both the short and
816 if(shortAddr != None):
818 # get a request number
819 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
824 # send an error message, could not get a sequence number to use at this time
825 sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
828 # get the info for sending
829 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
830 destShortAddr = hexStringToZigbeeHexString(shortAddr)
831 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
833 # create and send binding command
834 zigbeeConnectionMutex.acquire()
836 zigbeeConnection.send('tx_explicit',
838 # frame_id=chr(seqNumber),
839 dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
840 dest_addr='\xff\xfe',
842 dest_endpoint=dstEndpoint,
848 print '> BroadcastingRouteRecordReq is sent'
850 zigbeeConnectionMutex.release()
854 # send a fail response
855 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
860 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
862 global zigbeeLongShortAddr
863 global zigbeeLongShortAddrMutex
864 global zigeeBindRequestMutex
865 global zigeeBindRequest
866 global zigbeeConnectionMutex
867 global zigbeeConnection
868 global matchDescriptorReqSingleton
871 # get the short address for this device long address if possible
872 zigbeeLongShortAddrMutex.acquire()
873 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
874 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
875 zigbeeLongShortAddrMutex.release()
878 # if there is a short address than we can send the message
879 # if there is not one then we cannot since we need both the short and
881 if(shortAddr != None):
883 # get a request number
884 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
889 # send an error message, could not get a sequence number to use at this time
890 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
893 # get the info for sending
894 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
895 destShortAddr = hexStringToZigbeeHexString(shortAddr)
896 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
898 # create the payload data
900 payloadData += chr(seqNumber)
901 payloadData += '\x5a'
902 payloadData += '\x00'
904 # create and send binding command
905 zigbeeConnectionMutex.acquire()
906 zigbeeConnection.send('tx_explicit',
908 # frame_id=chr(seqNumber),
909 dest_addr_long=destLongAddr,
910 dest_addr=destShortAddr,
912 dest_endpoint='\x00',
917 print '> ManagementPermitJoiningReq is sent'
920 matchDescriptorReqSingleton= False
921 zigbeeConnectionMutex.release()
925 # send a fail response
926 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
930 def processUdpZclReadAttributesMessage(parsedData, addr):
931 ''' Method handle a ZCL read attribute command
933 parsedData -- Pre-parsed Data that was in the UDP packet.
934 addr -- Address (IP and Port) of the UDP packet origin.
937 global zigbeeLongShortAddr
938 global zigbeeLongShortAddrMutex
939 global zigeeBindRequestMutex
940 global zigeeBindRequest
941 global zigbeeConnectionMutex
942 global zigbeeConnection
946 if(zigbeeAddressAuthorityDict.has_key(addr)):
947 l = zigbeeAddressAuthorityDict[addr]
948 if(parsedData['device_address_long'] not in l):
956 # get the short address for this device long address if possible
957 zigbeeLongShortAddrMutex.acquire()
958 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
959 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
960 zigbeeLongShortAddrMutex.release()
963 # if there is a short address than we can send the message
964 # if there is not one then we cannot since we need both the short and
966 if(shortAddr != None):
968 # get a request number
969 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
974 # send an error message, could not get a sequence number to use at this time
975 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
978 # get the info for sending
979 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
980 destShortAddr = hexStringToZigbeeHexString(shortAddr)
981 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
982 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
983 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
985 # get all the attributes
986 attributeIds = parsedData['attribute_ids'].split(',')
988 # create the payload data
990 payloadData += '\x00'
991 payloadData += chr(seqNumber)
992 payloadData += '\x00'
994 # make all the attributes payloads
995 for attr in attributeIds:
997 attr = changeEndian(attr)
998 payloadData += hexStringToZigbeeHexString(attr)
1000 # create and send binding command
1001 zigbeeConnectionMutex.acquire()
1002 zigbeeConnection.send('tx_explicit',
1004 # frame_id=chr(seqNumber),
1005 dest_addr_long=destLongAddr,
1006 dest_addr=destShortAddr,
1007 src_endpoint='\x00',
1008 dest_endpoint=dstEndpoint,
1013 zigbeeConnectionMutex.release()
1017 # send a fail response
1018 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1021 def processUdpZclConfigureReportingMessage(parsedData, addr):
1022 ''' Method handle a zcl configure reporting message
1024 parsedData -- Pre-parsed Data that was in the UDP packet.
1025 addr -- Address (IP and Port) of the UDP packet origin.
1028 global zigbeeLongShortAddr
1029 global zigbeeLongShortAddrMutex
1030 global zigeeBindRequestMutex
1031 global zigeeBindRequest
1032 global zigbeeConnectionMutex
1033 global zigbeeConnection
1035 if(zigbeeAddressAuthorityDict.has_key(addr)):
1036 l = zigbeeAddressAuthorityDict[addr]
1037 if(parsedData['device_address_long'] not in l):
1045 # get the short address for this device long address if possible
1046 zigbeeLongShortAddrMutex.acquire()
1047 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1048 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1049 zigbeeLongShortAddrMutex.release()
1051 # if there is a short address than we can send the message
1052 # if there is not one then we cannot since we need both the short and
1054 if(shortAddr != None):
1056 # get a request number
1057 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1060 if(seqNumber == -1):
1061 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1064 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1065 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1066 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1067 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1068 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1070 # create the payload data
1072 payloadData += '\x00'
1073 payloadData += chr(seqNumber)
1074 payloadData += '\x06'
1075 payloadData += '\x00'
1076 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1077 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1078 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1079 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1081 if(parsedData.has_key('reportable_change')):
1082 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1085 # create and send binding command
1086 zigbeeConnectionMutex.acquire()
1087 zigbeeConnection.send('tx_explicit',
1089 # frame_id=chr(seqNumber),
1090 dest_addr_long=destLongAddr,
1091 dest_addr=destShortAddr,
1092 src_endpoint='\x00',
1093 dest_endpoint=dstEndpoint,
1098 zigbeeConnectionMutex.release()
1102 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1106 def processUdpPolicySet(parsedData, addr):
1107 ''' Method handle a policy set message
1109 parsedData -- Pre-parsed Data that was in the UDP packet.
1110 addr -- Address (IP and Port) of the UDP packet origin.
1112 print "=================================================================="
1113 print "Policy set: ", parsedData
1114 print 'addr : ', addr
1117 # do nothing if wrong source
1118 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 :
1119 #if addr == SYSTEM_MASTER_ADDRESS :
1120 if addr[0] == SYSTEM_MASTER_ADDRESS[0]:
1121 key = (parsedData['ip_address'], int(parsedData['port']))
1122 if (zigbeeAddressAuthorityDict.has_key(key)):
1123 zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1125 zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1128 def processUdpPolicyClear(parsedData, addr):
1129 ''' Method handle a policy set message
1131 parsedData -- Pre-parsed Data that was in the UDP packet.
1132 addr -- Address (IP and Port) of the UDP packet origin.
1134 print "=================================================================="
1135 print "Clear policy: ", parsedData
1137 # do nothing if wrong source
1138 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1139 if addr == SYSTEM_MASTER_ADDRESS :
1140 zigbeeAddressAuthorityDict.clear()
1147 def processZigbeeATCommandMessage(parsedData):
1148 ''' Method to process an AT zigbee message
1150 parsedData -- Pre-parsed (into a dict) data from message.
1152 global ZIGBEE_DEVICE_ADDRESS
1153 global didGetLocalRadioHighAddress
1154 global didGetLocalRadioLowAddress
1156 # command response for the high bytes of the local device long address
1157 if(parsedData['command'] == 'SH'):
1158 # convert the parameter to a string value (human readable)
1160 for e in parsedData['parameter']:
1161 value += "{0:02x}".format(ord(e))
1163 # set the correct portion of the address
1164 ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1166 #signal that we got this part of the address
1167 didGetLocalRadioHighAddress = True
1169 # command response for the low bytes of the local device long address
1170 elif(parsedData['command'] == 'SL'):
1171 # convert the parameter to a string value (human readable)
1173 for e in parsedData['parameter']:
1174 value += "{0:02x}".format(ord(e))
1176 # set the correct portion of the address
1177 ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1179 #signal that we got this part of the address
1180 didGetLocalRadioLowAddress = True
1182 def processZigbeeRxExplicitCommandMessage(parsedData):
1183 ''' Method to process a rx-explicit zigbee message
1185 parsedData -- Pre-parsed (into a dict) data from message.
1187 global zigeeBindRequestMutex
1188 global zigeeBindRequest
1189 global zigbeeConnectionMutex
1190 global zigbeeConnection
1191 global ManagementPermitJoiningReqSuccess
1193 # get the long and short addresses from the message payload since we can
1194 # use these to update the short addresses since this short address is fresh
1195 longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1196 shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1198 # check if this short address is for a device that has yet to be
1200 zigbeeUnregisteredAddressesMutex.acquire()
1201 if(longAddr in zigbeeUnregisteredAddresses):
1202 zigbeeUnregisteredAddresses.remove(longAddr)
1203 zigbeeUnregisteredAddressesMutex.release()
1205 # update/ or insert the short address
1206 zigbeeLongShortAddrMutex.acquire()
1207 zigbeeLongShortAddr[longAddr] = shortAddr
1208 zigbeeLongShortAddrMutex.release()
1210 global matchDescriptorReqSingleton
1211 global deviceAnnouncementSingleton
1212 global seqNumberForNotification
1214 # if this is a ZDO message/response
1215 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1217 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1218 if(parsedData['profile'] == '\x00\x00'):
1221 # if this is a Match Descriptor Request so we need to answer.
1222 if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1223 zigbeeConnectionMutex.acquire()
1224 zigbeeConnection.send('tx_explicit',
1226 # frame_id=chr(seqNumber),
1227 dest_addr_long=parsedData['source_addr_long'],
1228 dest_addr=parsedData['source_addr'],
1229 src_endpoint='\x00',
1230 dest_endpoint='\x00',
1233 data=parsedData['rf_data']
1236 zigbeeConnection.send('tx_explicit',
1238 # frame_id=chr(seqNumber),
1239 dest_addr_long=parsedData['source_addr_long'],
1240 dest_addr=parsedData['source_addr'],
1241 src_endpoint='\x00',
1242 dest_endpoint='\x00',
1245 data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1249 print '[ 0x0006 ] Match Descriptor Request - answered'
1250 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1251 zigbeeConnectionMutex.release()
1254 # if this is a device announcement so we can get some useful data from it
1255 elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1257 # pick out the correct parts of the payload
1258 longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1259 shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1261 # change the endian of the address
1262 longAddr = changeEndian(longAddr)
1263 shortAddr = changeEndian(shortAddr)
1265 # update the table with the new information
1266 zigbeeLongShortAddrMutex.acquire()
1267 zigbeeLongShortAddr[longAddr] = shortAddr
1268 zigbeeLongShortAddrMutex.release()
1270 # check if this short address is for a device that has yet to be
1272 zigbeeUnregisteredAddressesMutex.acquire()
1273 if(longAddr in zigbeeUnregisteredAddresses):
1274 zigbeeUnregisteredAddresses.remove(longAddr)
1275 zigbeeUnregisteredAddressesMutex.release()
1279 zigbeeConnectionMutex.acquire()
1280 zigbeeConnection.send('tx_explicit',
1282 # frame_id=chr(seqNumber),
1283 dest_addr_long=parsedData['source_addr_long'],
1284 dest_addr=parsedData['source_addr'],
1285 src_endpoint='\x00',
1286 dest_endpoint='\x00',
1289 data=parsedData['rf_data']
1292 print '[ 0x0013 ] device announcement - answered'
1293 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1294 deviceAnnouncementSingleton = False
1295 zigbeeConnectionMutex.release()
1298 # if this is a response to a zdo bind_req message
1299 elif(parsedData['cluster'] == '\x80\x21'):
1301 # get the status and sequence number from the message
1302 seqNumber = parsedData['rf_data'][0]
1303 statusCode = parsedData['rf_data'][1]
1304 print ">response to a zdo bind_req message parsedData>"
1306 # get the bind tuple information
1307 # for this specific bind request
1309 zigeeBindRequestMutex.acquire()
1310 if(zigeeBindRequest.has_key(ord(seqNumber))):
1311 tup = zigeeBindRequest[ord(seqNumber)]
1312 zigeeBindRequestMutex.release()
1315 # cant really do anything in this case...
1316 # don't have any information on who the data is for
1319 # successful binding
1320 if(ord(statusCode) == 0):
1322 # add a callback for this specific device and cluster
1323 # to the HA callback dict
1324 zibeeHACallbackMutex.acquire();
1325 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1326 if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1327 zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1329 zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1330 zibeeHACallbackMutex.release()
1332 # send success message
1333 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1336 elif (ord(statusCode) == 170):
1337 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1340 elif (ord(statusCode) == 174):
1341 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1343 # Other issue, dont have code for
1345 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1347 # if this is a response to a short address query
1348 elif(parsedData['cluster'] == '\x80\x00'):
1349 print ">response to a short address query 0x8000"
1352 statusCode = parsedData['rf_data'][0]
1354 # does not matter if this is not a success, we can try again later
1355 if(statusCode != '\x00'):
1356 # status code was not success so do not do anything
1359 # get the short and long address information
1360 longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1361 shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1363 # remove device from list of unregistered devices if it is in it
1364 zigbeeUnregisteredAddressesMutex.acquire()
1365 if(longAddr in zigbeeUnregisteredAddresses):
1366 zigbeeUnregisteredAddresses.remove(longAddr)
1367 zigbeeUnregisteredAddressesMutex.release()
1369 # update/insert the short address
1370 zigbeeLongShortAddrMutex.acquire()
1371 zigbeeLongShortAddr[longAddr] = shortAddr
1372 zigbeeLongShortAddrMutex.release()
1375 elif(parsedData['cluster'] == '\x80\x06'):
1377 print '[ 0x8006 ] get Match Descriptor Response'
1378 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1381 elif(parsedData['cluster'] == '\x80\x36'):
1383 print '[ 0x8036 ] get Management Permit Joining Response'
1384 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1386 ManagementPermitJoiningReqSuccess = True
1391 print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1392 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1395 # if this is a home automation zcl message/response
1396 elif (parsedData['profile'] == '\x01\x04'):
1398 # get the zcl message header
1399 zclFrameControl = parsedData['rf_data'][0]
1400 zclSeqNumber = parsedData['rf_data'][1]
1401 zclCommand = parsedData['rf_data'][2]
1402 zclStatus = parsedData['rf_data'][3]
1405 if(zclCommand == '\x00'):
1407 print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1409 # find who to send response
1411 zigbeeSeqNumberToClientMutex.acquire()
1413 if(longAddr in seqNumberForNotification):
1415 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1416 tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1417 #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1418 zigbeeSeqNumberToClientMutex.release()
1420 # no one to send the response to so just move on
1422 # cant really do anything here
1424 # create the response message
1426 message = "type : zcl_zone_status_change_notification\n"
1427 message += "packet_id: " + packetId + "\n"
1428 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1429 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1430 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1431 message += "attributes: success"
1434 sendSoceket.sendto(message,tup[0])
1435 print(">port : ", tup[0][1])
1439 # this is a zcl read attribute response
1440 elif(zclCommand == '\x01'):
1442 # get the zcl payload
1443 zclPayload = parsedData['rf_data'][3:]
1444 attibuteResponseList = []
1446 # get the data for each data
1447 while(len(zclPayload) > 0):
1448 attributeId = zclPayload[0:2]
1449 attributeStatus = zclPayload[2]
1450 zclPayload = zclPayload[3:]
1452 if(ord(attributeStatus) != 0):
1453 # if attribute is not supported then it has no data
1454 # package the data and add it to the list
1455 attibuteResponseList.append((attributeId,"not_supported"))
1458 # get the data type and data length of the attributre
1459 attributeType = zclPayload[0]
1460 dataLength = zclDataTypeToBytes(zclPayload)
1462 # consume zcl payload data
1463 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1464 zclPayload = zclPayload[2:]
1465 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1466 zclPayload = zclPayload[3:]
1468 zclPayload = zclPayload[1:]
1470 # package the data and add it to the list
1471 newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1472 attibuteResponseList.append(newData)
1474 # consume the data size of the payload
1475 zclPayload = zclPayload[dataLength:]
1477 # find who to send response to
1479 zigbeeSeqNumberToClientMutex.acquire()
1480 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1481 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1482 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1483 zigbeeSeqNumberToClientMutex.release()
1485 # no one to send the response to so just move on
1487 # cant really do anything here
1490 # create the response message
1492 message = "type : zcl_read_attributes_response \n"
1493 message += "packet_id: " + packetId + "\n"
1494 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1495 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1496 message += "attributes: "
1498 # create the message for each attribute
1499 for t in attibuteResponseList:
1500 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1501 if(t[1] == "success"):
1502 attrIdStr = "%0.4x" % attrId
1503 attrIdStr = changeEndian(attrIdStr)
1505 message += attrIdStr
1507 message += "success"
1509 message += "%0.2x" % ord(t[2])
1514 dat += "%0.2x" % ord(c)
1515 dat = changeEndian(dat)
1519 attrIdStr = "%0.4x" % attrId
1520 attrIdStr = changeEndian(attrIdStr)
1522 message += attrIdStr
1524 message += "not_supported"
1527 message = message[0:len(message) - 1]
1530 sendSoceket.sendto(message,tup[0])
1536 # this is a zcl write attribute response
1537 elif(zclCommand == '\x04'):
1539 # get the zcl payload
1540 zclPayload = parsedData['rf_data'][3]
1541 # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1542 if(zclStatus == '\x70' or zclPayload == '\x00'):
1544 # find who to send response to
1546 zigbeeSeqNumberToClientMutex.acquire()
1547 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1548 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1549 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1550 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1551 zigbeeSeqNumberToClientMutex.release()
1552 # no one to send the response to so just move on
1554 # cant really do anything here
1557 # create the response message
1559 message = "type : zcl_write_attributes_response\n"
1560 message += "packet_id: " + packetId + "\n"
1561 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1562 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1563 message += "attributes: success"
1566 sendSoceket.sendto(message,tup[0])
1568 print '[ 0x0500 ] get Write Attribute Response success'
1569 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1573 print '[ 0x0500 ] get Write Attribute Response'
1574 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1578 # this is a zcl configure attribute response
1579 elif(zclCommand == '\x07'):
1581 # find who to send response to
1583 zigbeeSeqNumberToClientMutex.acquire()
1584 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1585 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1586 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1587 zigbeeSeqNumberToClientMutex.release()
1589 # no one to send the response to so just move on
1591 # cant really do anything here
1595 zclPayload = parsedData['rf_data'][3:]
1597 # construct the message
1599 message = "type : zcl_configure_reporting_response \n"
1600 message += "packet_id: " + packetId + "\n"
1601 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1602 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1603 message += "attributes: "
1605 if(len(zclPayload) == 1):
1606 # if all the configurations are a success then only send back a success
1607 # based on zigbee specs
1608 message += "all_success \n";
1609 sendSoceket.sendto(message,tup[0])
1612 attibuteResponseList = []
1614 # get each attributes data
1615 while(len(zclPayload) > 0):
1616 attributeStatus = zclPayload[0]
1617 attributeDirection = zclPayload[1]
1618 attributeId = zclPayload[2:4]
1619 zclPayload = zclPayload[4:]
1621 newData = (attributeStatus,attributeDirection, attributeId)
1622 attibuteResponseList.append(newData)
1624 # package each attribute
1625 for t in attibuteResponseList:
1626 attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1627 attrIdStr = "%0.4x" % attrId
1628 attrIdStr = changeEndian(attrIdStr)
1630 message += attrIdStr
1633 message += "success"
1640 message += "reported"
1642 message += "received"
1645 message = message[0:len(message) - 1]
1647 sendSoceket.sendto(message,tup[0])
1649 # this is a zcl report attribute message
1650 elif(zclCommand == '\x0a'):
1651 print "get Report attribute "
1652 # get teh zcl payload
1653 zclPayload = parsedData['rf_data'][3:]
1654 attibuteResponseList = []
1656 # extract the attribute data
1657 while(len(zclPayload) > 0):
1658 attributeId = zclPayload[0:2]
1659 zclPayload = zclPayload[2:]
1660 attributeType = zclPayload[0]
1661 dataLength = zclDataTypeToBytes(zclPayload)
1663 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1664 zclPayload = zclPayload[2:]
1665 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1666 zclPayload = zclPayload[3:]
1668 zclPayload = zclPayload[1:]
1670 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1671 attibuteResponseList.append(newData)
1672 zclPayload = zclPayload[dataLength:]
1675 # get callback clients to respond to
1676 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1678 zibeeHACallbackMutex.acquire()
1679 if(zibeeHACallback.has_key(callbackIndex)):
1680 retAddr = zibeeHACallback[callbackIndex]
1681 zibeeHACallbackMutex.release()
1683 # no one to respond to so do nothing here
1684 if(retAddr == None):
1687 # construct the message
1688 message = "type : zcl_report_attributes \n"
1689 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1690 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1691 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1692 message += "attributes: "
1694 # package the attributes
1695 for t in attibuteResponseList:
1696 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1697 attrIdStr = "%0.4x" % attrId
1698 attrIdStr = changeEndian(attrIdStr)
1700 message += attrIdStr
1702 message += "%0.2x" % ord(t[1])
1707 dat += "%0.2x" % ord(c)
1708 dat = changeEndian(dat)
1712 message = message[0:len(message) - 1]
1714 print "Sending", message
1716 # send to all client that want this callback
1718 sendSoceket.sendto(message,ra)
1720 # -----------------------------------------------------------------------------
1721 # Communication Callback/Parse Methods
1722 # -----------------------------------------------------------------------------
1723 def handleNewZigbeeMessage(parsedData):
1724 ''' Method to process a zigbee message from the local radio.
1726 parsedData -- Pre-parsed (into a dict) data from message.
1728 #print "=================================================================="
1730 print "New Zigbee Message"
1731 #printMessageData(parsedData)
1733 # dispatch to the correct zigbee handler
1734 if (parsedData['id'] == 'at_response'):
1735 print "parsedDataID : at_response"
1736 processZigbeeATCommandMessage(parsedData)
1738 elif (parsedData['id'] == 'rx_explicit'):
1739 print "parsedDataID : rx_explicit"
1740 processZigbeeRxExplicitCommandMessage(parsedData)
1743 print "Unknown API format"
1745 #print "=================================================================="
1747 def handleNewUdpPacket(data, addr):
1748 ''' Method to parse and handle an incoming UDP packet.
1750 data -- Data that was in the UDP packet.
1751 addr -- Address (IP and Port) of the UDP packet origin.
1753 global ManagementPermitJoiningReqSuccess
1755 #print "=================================================================="
1757 #print "Got New UDP packet..."
1761 # data comes in as 'key: value\n key: value...' string and so needs to be
1762 # parsed into a dict
1765 # 1 key, value pair per line
1766 for line in data.split('\n'):
1768 # key and values are split based on a ':'
1769 fields = line.split(':')
1771 # make sure properly formated otherwise just ignore it
1772 if len(fields) == 2:
1774 # do strips to remove any white spacing that may have resulted
1775 # from improper packing on the sender side
1776 parsedData[fields[0].strip()] = fields[1].strip()
1779 # wrap in try statement just in case there is an improperly formated packet we
1782 # dispatch to the correct process method
1783 if(parsedData["type"] == "zdo_bind_request"):
1784 print "> processUdpZdoBindReqMessage call"
1785 processUdpZdoBindReqMessage(parsedData, addr)
1786 elif(parsedData["type"] == "zdo_unbind_request"):
1787 processUdpZdoUnBindReqMessage(parsedData, addr)
1788 elif(parsedData["type"] == "send_address"):
1789 print "> processUdpSendAddressMessage call"
1790 processUdpSendAddressMessage(parsedData, addr)
1791 elif(parsedData["type"] == "zcl_read_attributes"):
1792 processUdpZclReadAttributesMessage(parsedData, addr)
1793 elif(parsedData["type"] == "zcl_configure_reporting"):
1794 print "> zcl_configure_reporting call"
1795 processUdpZclConfigureReportingMessage(parsedData, addr)
1796 elif(parsedData["type"] == "policy_set"):
1797 processUdpPolicySet(parsedData, addr)
1798 elif(parsedData["type"] == "policy_clear"):
1799 processUdpPolicyClear(parsedData, addr)
1800 elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo
1801 processUdpManagementPermitJoiningReqMessage(parsedData, addr)
1802 elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo
1803 processUdpZclWriteAttributesMessage(parsedData, addr)
1804 elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo
1805 processUdpEnrollmentResponse(parsedData, addr)
1806 elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo
1807 processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
1808 elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo
1809 processUdpZclChangeSwitchReqMessage(parsedData, addr)
1811 print "unknown Packet: " + parsedData["type"]
1814 # if we ever get here then something went wrong and so just ignore this
1815 # packet and try again later
1816 print "I didn't expect this error:", sys.exc_info()[0]
1817 traceback.print_exc()
1819 #print "=================================================================="
1822 # -----------------------------------------------------------------------------
1823 # Main Running Methods
1824 # -----------------------------------------------------------------------------
1827 '''Main function used for starting the application as the main driver'''
1829 global ZIGBEE_SERIAL_PORT
1830 global ZIGBEE_SERIAL_BAUD
1831 global UDP_RECEIVE_PORT
1832 global zigbeeConnection
1836 parseCommandLineArgs(sys.argv[1:])
1838 # create serial object used for communication to the zigbee radio
1839 sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
1841 # create a zigbee object that handles all zigbee communication
1842 # we use this to do all communication to and from the radio
1843 # when data comes from the radio it will get a bit of unpacking
1844 # and then a call to the callback specified will be done with the
1846 zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
1848 # get the long address of our local radio before we start doing anything
1849 getConnectedRadioLongAddress();
1851 # setup incoming UDP socket and bind it to self and specified UDP port
1852 # sending socket does not need to be bound to anything
1853 #receiveSoceket.bind(('192.168.2.227', UDP_RECEIVE_PORT))
1854 receiveSoceket.bind(('192.168.2.192', UDP_RECEIVE_PORT))
1856 # create the thread that does short address lookups
1857 addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
1858 addressUpdateWorkerThread.start()
1863 print "=================================================================="
1866 print "=================================================================="
1868 # wait for an incoming UDP packet
1869 # this is a blocking call
1870 data, addr = receiveSoceket.recvfrom(4096)
1872 # handle the UDP packet appropriately
1873 handleNewUdpPacket(data, addr)
1875 except KeyboardInterrupt:
1876 # use the keyboard interupt to catch a ctrl-c and kill the application
1880 # something went really wrong and so exit with error message
1881 traceback.print_exc()
1883 # signal all threads to exit
1886 # wait for threads to finish before closing of the resources
1887 addressUpdateWorkerThread.join()
1890 # make sure to close all the connections
1891 zigbeeConnection.halt()
1892 receiveSoceket.close()
1895 if __name__ == "__main__":
1896 # call main function since this is being run as the start