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
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')
794 def processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr):
796 global zigbeeLongShortAddr
797 global zigbeeLongShortAddrMutex
798 global zigeeBindRequestMutex
799 global zigeeBindRequest
800 global zigbeeConnectionMutex
801 global zigbeeConnection
804 # get the short address for this device long address if possible
805 zigbeeLongShortAddrMutex.acquire()
806 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
807 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
808 zigbeeLongShortAddrMutex.release()
811 # if there is a short address than we can send the message
812 # if there is not one then we cannot since we need both the short and
814 if(shortAddr != None):
816 # get a request number
817 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
822 # send an error message, could not get a sequence number to use at this time
823 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'out_of_space')
826 # get the info for sending
827 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
828 destShortAddr = hexStringToZigbeeHexString(shortAddr)
829 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
830 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
831 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
832 value = hexStringToZigbeeHexString(parsedData['value'])
834 # create and send binding command
835 zigbeeConnectionMutex.acquire()
837 zigbeeConnection.send('tx_explicit',
839 dest_addr_long=destLongAddr,
840 dest_addr=destShortAddr,
842 dest_endpoint=dstEndpoint,
845 data='\x01'+chr(seqNumber)+value
849 print '> The door lock is unlocked'
850 elif value == '\x00':
851 print '> The door lock is locked'
853 print '> Unknown door lock value: ' + str(value)
855 zigbeeConnectionMutex.release()
859 # send a fail response
860 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'short_address_unknown')
863 def processUdpZclReadDoorStatusReqMessage(parsedData, addr):
865 global zigbeeLongShortAddr
866 global zigbeeLongShortAddrMutex
867 global zigeeBindRequestMutex
868 global zigeeBindRequest
869 global zigbeeConnectionMutex
870 global zigbeeConnection
873 # get the short address for this device long address if possible
874 zigbeeLongShortAddrMutex.acquire()
875 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
876 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
877 zigbeeLongShortAddrMutex.release()
880 # if there is a short address than we can send the message
881 # if there is not one then we cannot since we need both the short and
883 if(shortAddr != None):
885 # get a request number
886 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
891 # send an error message, could not get a sequence number to use at this time
892 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'out_of_space')
895 # get the info for sending
896 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
897 destShortAddr = hexStringToZigbeeHexString(shortAddr)
898 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
899 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
900 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
901 # framecontrol = hexStringToZigbeeHexString(parsedData['framecontrol'])
902 # commandframe = hexStringToZigbeeHexString(parsedData['commandframe'])
903 # attribute_id = hexStringToZigbeeHexString(parsedData['attribute_id'])
905 # create and send binding command
906 zigbeeConnectionMutex.acquire()
908 zigbeeConnection.send('tx_explicit',
910 dest_addr_long=destLongAddr,
911 dest_addr=destShortAddr,
913 dest_endpoint=dstEndpoint,
916 data='\x10' + chr(seqNumber) + '\x00' + '\x00\x00'
920 zigbeeConnectionMutex.release()
921 print "send read door status"
924 # send a fail response
925 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'short_address_unknown')
928 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
930 global zigbeeLongShortAddr
931 global zigbeeLongShortAddrMutex
932 global zigeeBindRequestMutex
933 global zigeeBindRequest
934 global zigbeeConnectionMutex
935 global zigbeeConnection
938 # get the short address for this device long address if possible
939 zigbeeLongShortAddrMutex.acquire()
940 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
941 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
942 zigbeeLongShortAddrMutex.release()
945 # if there is a short address than we can send the message
946 # if there is not one then we cannot since we need both the short and
948 if(shortAddr != None):
950 # get a request number
951 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
956 # send an error message, could not get a sequence number to use at this time
957 sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
960 # get the info for sending
961 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
962 destShortAddr = hexStringToZigbeeHexString(shortAddr)
963 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
965 # create and send binding command
966 zigbeeConnectionMutex.acquire()
968 zigbeeConnection.send('tx_explicit',
970 # frame_id=chr(seqNumber),
971 dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
972 dest_addr='\xff\xfe',
974 dest_endpoint=dstEndpoint,
980 print '> BroadcastingRouteRecordReq is sent'
982 zigbeeConnectionMutex.release()
986 # send a fail response
987 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
992 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
994 global zigbeeLongShortAddr
995 global zigbeeLongShortAddrMutex
996 global zigeeBindRequestMutex
997 global zigeeBindRequest
998 global zigbeeConnectionMutex
999 global zigbeeConnection
1000 global matchDescriptorReqSingleton
1003 # get the short address for this device long address if possible
1004 zigbeeLongShortAddrMutex.acquire()
1005 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1006 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1007 zigbeeLongShortAddrMutex.release()
1010 # if there is a short address than we can send the message
1011 # if there is not one then we cannot since we need both the short and
1013 if(shortAddr != None):
1015 # get a request number
1016 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1019 if(seqNumber == -1):
1021 # send an error message, could not get a sequence number to use at this time
1022 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
1025 # get the info for sending
1026 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1027 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1028 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1030 # create the payload data
1032 payloadData += chr(seqNumber)
1033 payloadData += '\x5a'
1034 payloadData += '\x00'
1036 # create and send binding command
1037 zigbeeConnectionMutex.acquire()
1038 zigbeeConnection.send('tx_explicit',
1040 # frame_id=chr(seqNumber),
1041 dest_addr_long=destLongAddr,
1042 dest_addr=destShortAddr,
1043 src_endpoint='\x00',
1044 dest_endpoint='\x00',
1049 print '> ManagementPermitJoiningReq is sent'
1052 matchDescriptorReqSingleton= False
1053 zigbeeConnectionMutex.release()
1057 # send a fail response
1058 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
1062 def processUdpZclReadAttributesMessage(parsedData, addr):
1063 ''' Method handle a ZCL read attribute command
1065 parsedData -- Pre-parsed Data that was in the UDP packet.
1066 addr -- Address (IP and Port) of the UDP packet origin.
1069 global zigbeeLongShortAddr
1070 global zigbeeLongShortAddrMutex
1071 global zigeeBindRequestMutex
1072 global zigeeBindRequest
1073 global zigbeeConnectionMutex
1074 global zigbeeConnection
1078 if(zigbeeAddressAuthorityDict.has_key(addr)):
1079 l = zigbeeAddressAuthorityDict[addr]
1080 if(parsedData['device_address_long'] not in l):
1088 # get the short address for this device long address if possible
1089 zigbeeLongShortAddrMutex.acquire()
1090 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1091 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1092 zigbeeLongShortAddrMutex.release()
1095 # if there is a short address than we can send the message
1096 # if there is not one then we cannot since we need both the short and
1098 if(shortAddr != None):
1100 # get a request number
1101 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1104 if(seqNumber == -1):
1106 # send an error message, could not get a sequence number to use at this time
1107 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
1110 # get the info for sending
1111 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1112 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1113 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1114 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1115 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1117 # get all the attributes
1118 attributeIds = parsedData['attribute_ids'].split(',')
1120 # create the payload data
1122 payloadData += '\x00'
1123 payloadData += chr(seqNumber)
1124 payloadData += '\x00'
1126 # make all the attributes payloads
1127 for attr in attributeIds:
1129 attr = changeEndian(attr)
1130 payloadData += hexStringToZigbeeHexString(attr)
1132 # create and send binding command
1133 zigbeeConnectionMutex.acquire()
1134 zigbeeConnection.send('tx_explicit',
1136 # frame_id=chr(seqNumber),
1137 dest_addr_long=destLongAddr,
1138 dest_addr=destShortAddr,
1139 src_endpoint='\x00',
1140 dest_endpoint=dstEndpoint,
1145 zigbeeConnectionMutex.release()
1149 # send a fail response
1150 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1153 def processUdpZclConfigureReportingMessage(parsedData, addr):
1154 ''' Method handle a zcl configure reporting message
1156 parsedData -- Pre-parsed Data that was in the UDP packet.
1157 addr -- Address (IP and Port) of the UDP packet origin.
1160 global zigbeeLongShortAddr
1161 global zigbeeLongShortAddrMutex
1162 global zigeeBindRequestMutex
1163 global zigeeBindRequest
1164 global zigbeeConnectionMutex
1165 global zigbeeConnection
1167 if(zigbeeAddressAuthorityDict.has_key(addr)):
1168 l = zigbeeAddressAuthorityDict[addr]
1169 if(parsedData['device_address_long'] not in l):
1177 # get the short address for this device long address if possible
1178 zigbeeLongShortAddrMutex.acquire()
1179 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1180 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1181 zigbeeLongShortAddrMutex.release()
1183 # if there is a short address than we can send the message
1184 # if there is not one then we cannot since we need both the short and
1186 if(shortAddr != None):
1188 # get a request number
1189 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1192 if(seqNumber == -1):
1193 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1196 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1197 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1198 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1199 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1200 srcEndpoint = hexStringToZigbeeHexString(parsedData['src_endpoint'])
1201 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1203 # create the payload data
1205 payloadData += '\x10'
1206 payloadData += chr(seqNumber)
1207 payloadData += '\x06'
1208 payloadData += '\x00'
1209 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1210 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1211 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1212 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1214 if(parsedData.has_key('reportable_change')):
1215 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1218 # create and send binding command
1219 zigbeeConnectionMutex.acquire()
1220 zigbeeConnection.send('tx_explicit',
1222 # frame_id=chr(seqNumber),
1223 dest_addr_long=destLongAddr,
1224 dest_addr=destShortAddr,
1225 src_endpoint=srcEndpoint,
1226 dest_endpoint=dstEndpoint,
1231 zigbeeConnectionMutex.release()
1235 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1239 def processUdpPolicySet(parsedData, addr):
1240 ''' Method handle a policy set message
1242 parsedData -- Pre-parsed Data that was in the UDP packet.
1243 addr -- Address (IP and Port) of the UDP packet origin.
1245 print "=================================================================="
1246 print "Policy set: ", parsedData
1247 print 'addr : ', addr
1250 # do nothing if wrong source
1251 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 :
1252 #if addr == SYSTEM_MASTER_ADDRESS :
1253 if addr[0] == SYSTEM_MASTER_ADDRESS[0]:
1254 key = (parsedData['ip_address'], int(parsedData['port']))
1255 if (zigbeeAddressAuthorityDict.has_key(key)):
1256 zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1258 zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1261 def processUdpPolicyClear(parsedData, addr):
1262 ''' Method handle a policy set message
1264 parsedData -- Pre-parsed Data that was in the UDP packet.
1265 addr -- Address (IP and Port) of the UDP packet origin.
1267 print "=================================================================="
1268 print "Clear policy: ", parsedData
1270 # do nothing if wrong source
1271 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1272 if addr == SYSTEM_MASTER_ADDRESS :
1273 zigbeeAddressAuthorityDict.clear()
1280 def processZigbeeATCommandMessage(parsedData):
1281 ''' Method to process an AT zigbee message
1283 parsedData -- Pre-parsed (into a dict) data from message.
1285 global ZIGBEE_DEVICE_ADDRESS
1286 global didGetLocalRadioHighAddress
1287 global didGetLocalRadioLowAddress
1289 # command response for the high bytes of the local device long address
1290 if(parsedData['command'] == 'SH'):
1291 # convert the parameter to a string value (human readable)
1293 for e in parsedData['parameter']:
1294 value += "{0:02x}".format(ord(e))
1296 # set the correct portion of the address
1297 ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1299 #signal that we got this part of the address
1300 didGetLocalRadioHighAddress = True
1302 # command response for the low bytes of the local device long address
1303 elif(parsedData['command'] == 'SL'):
1304 # convert the parameter to a string value (human readable)
1306 for e in parsedData['parameter']:
1307 value += "{0:02x}".format(ord(e))
1309 # set the correct portion of the address
1310 ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1312 #signal that we got this part of the address
1313 didGetLocalRadioLowAddress = True
1315 def processZigbeeRxExplicitCommandMessage(parsedData):
1316 ''' Method to process a rx-explicit zigbee message
1318 parsedData -- Pre-parsed (into a dict) data from message.
1320 global zigeeBindRequestMutex
1321 global zigeeBindRequest
1322 global zigbeeConnectionMutex
1323 global zigbeeConnection
1324 global ManagementPermitJoiningReqSuccess
1326 # get the long and short addresses from the message payload since we can
1327 # use these to update the short addresses since this short address is fresh
1328 longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1329 shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1331 # check if this short address is for a device that has yet to be
1333 zigbeeUnregisteredAddressesMutex.acquire()
1334 if(longAddr in zigbeeUnregisteredAddresses):
1335 zigbeeUnregisteredAddresses.remove(longAddr)
1336 zigbeeUnregisteredAddressesMutex.release()
1338 # update/ or insert the short address
1339 zigbeeLongShortAddrMutex.acquire()
1340 zigbeeLongShortAddr[longAddr] = shortAddr
1341 zigbeeLongShortAddrMutex.release()
1343 global matchDescriptorReqSingleton
1344 global deviceAnnouncementSingleton
1345 global seqNumberForNotification
1349 if (parsedData['cluster'] == '\x01\x01' and parsedData['profile'] == '\x01\x04'):
1350 zclSeqNumber = parsedData['rf_data'][1]
1352 zigbeeSeqNumberToClientMutex.acquire()
1353 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1354 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1355 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1356 zigbeeSeqNumberToClientMutex.release()
1358 rfdata = parsedData['rf_data']
1359 framecontrol = rfdata[0]
1365 if framecontrol == '\x19':
1366 if(command == '\x00'):
1368 print "( 0x0101 ) Door Lock: Lock Door Response"
1369 print time.strftime("%H:%M:%S", time.localtime())
1371 if(value == '\x00'):
1372 print "Door locked successfully"
1374 print "An error occurred in door locking"
1375 elif(command == '\x01'):
1377 print "( 0x0101 ) Door Lock: Unlock Door Response"
1378 print time.strftime("%H:%M:%S", time.localtime())
1380 if(value == '\x00'):
1381 print "Door unlocked successfully"
1383 print "An error occurred in door unlocking"
1385 elif framecontrol == '\x18':
1386 if(command == '\x01'):
1387 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1388 if attributeId == 0x0000:
1390 print "Door status: "
1392 print "Not fully locked"
1393 elif value == '\x01':
1395 elif value == '\x02':
1398 print "Unknown value: " + zigbeeHexStringToHexString(value)
1400 message = "type : zcl_read_attributes_response \n"
1401 message += "packet_id: " + packetId + "\n"
1402 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1403 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1404 message += "attributes: "
1406 attrIdStr = "%0.4x" % attributeId
1407 attrIdStr = changeEndian(attrIdStr)
1408 message += attrIdStr
1411 zclPayload = parsedData['rf_data'][3:]
1412 zclPayload = zclPayload[3:]
1413 attributeType = zclPayload[0]
1414 message += "%0.2x" % ord(attributeType)
1417 message += "success"
1420 message += "%0.2x" % ord(value)
1425 message = message[0:len(message) - 1]
1428 # no one to send the response to so just move on
1430 # cant really do anything here
1432 sendSoceket.sendto(message,tup[0])
1433 elif command == '\x07':
1436 print "( 0x0101 ) Door Lock: Configure reporting response"
1437 print 'rfdata : ' + zigbeeHexStringToHexString(rfdata)
1438 if status == '\x00':
1439 print "Configure report successfully"
1440 message = "type : zcl_configure_reporting_response \n"
1441 message += "packet_id: " + packetId + "\n"
1442 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1443 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1444 message += "attributes: "
1445 message += "all_success \n";
1447 # no one to send the response to so just move on
1449 # cant really do anything here
1451 sendSoceket.sendto(message,tup[0])
1453 print "Configure report unsuccessfully, status =", zigbeeHexStringToHexString(status)
1454 elif(command == '\x0A'):
1456 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1457 if attributeId == 0x0000:
1460 print "Not fully locked"
1461 elif value == '\x01':
1463 elif value == '\x02':
1466 print "Unknown value: " + zigbeeHexStringToHexString(value)
1468 message = "type : zcl_read_attributes_response \n"
1469 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1470 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1471 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1472 message += "attributes: "
1474 attrIdStr = "%0.4x" % attributeId
1475 attrIdStr = changeEndian(attrIdStr)
1476 message += attrIdStr
1479 zclPayload = parsedData['rf_data'][3:]
1480 zclPayload = zclPayload[3:]
1481 attributeType = zclPayload[0]
1482 message += "%0.2x" % ord(attributeType)
1485 message += "success"
1488 message += "%0.2x" % ord(value)
1493 message = message[0:len(message) - 1]
1496 # get callback clients to respond to
1497 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1499 zibeeHACallbackMutex.acquire()
1500 if(zibeeHACallback.has_key(callbackIndex)):
1501 retAddr = zibeeHACallback[callbackIndex]
1502 zibeeHACallbackMutex.release()
1504 # no one to respond to so do nothing here
1505 if(retAddr == None):
1508 sendSoceket.sendto(message,ra)
1511 # if this is a ZDO message/response
1512 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1514 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1515 if(parsedData['profile'] == '\x00\x00'):
1518 # if this is a Match Descriptor Request so we need to answer.
1519 if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1520 zigbeeConnectionMutex.acquire()
1521 zigbeeConnection.send('tx_explicit',
1523 # frame_id=chr(seqNumber),
1524 dest_addr_long=parsedData['source_addr_long'],
1525 dest_addr=parsedData['source_addr'],
1526 src_endpoint='\x00',
1527 dest_endpoint='\x00',
1530 data=parsedData['rf_data']
1533 zigbeeConnection.send('tx_explicit',
1535 # frame_id=chr(seqNumber),
1536 dest_addr_long=parsedData['source_addr_long'],
1537 dest_addr=parsedData['source_addr'],
1538 src_endpoint='\x00',
1539 dest_endpoint='\x00',
1542 data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1546 print '[ 0x0006 ] Match Descriptor Request - answered'
1547 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1548 zigbeeConnectionMutex.release()
1551 # if this is a device announcement so we can get some useful data from it
1552 elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1554 # pick out the correct parts of the payload
1555 longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1556 shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1558 # change the endian of the address
1559 longAddr = changeEndian(longAddr)
1560 shortAddr = changeEndian(shortAddr)
1562 # update the table with the new information
1563 zigbeeLongShortAddrMutex.acquire()
1564 zigbeeLongShortAddr[longAddr] = shortAddr
1565 zigbeeLongShortAddrMutex.release()
1567 # check if this short address is for a device that has yet to be
1569 zigbeeUnregisteredAddressesMutex.acquire()
1570 if(longAddr in zigbeeUnregisteredAddresses):
1571 zigbeeUnregisteredAddresses.remove(longAddr)
1572 zigbeeUnregisteredAddressesMutex.release()
1576 zigbeeConnectionMutex.acquire()
1577 zigbeeConnection.send('tx_explicit',
1579 # frame_id=chr(seqNumber),
1580 dest_addr_long=parsedData['source_addr_long'],
1581 dest_addr=parsedData['source_addr'],
1582 src_endpoint='\x00',
1583 dest_endpoint='\x00',
1586 data=parsedData['rf_data']
1589 print '[ 0x0013 ] device announcement - answered'
1590 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1591 deviceAnnouncementSingleton = False
1592 zigbeeConnectionMutex.release()
1595 # if this is a response to a zdo bind_req message
1596 elif(parsedData['cluster'] == '\x80\x21'):
1598 # get the status and sequence number from the message
1599 seqNumber = parsedData['rf_data'][0]
1600 statusCode = parsedData['rf_data'][1]
1601 print ">response to a zdo bind_req message parsedData>"
1603 # get the bind tuple information
1604 # for this specific bind request
1606 zigeeBindRequestMutex.acquire()
1607 if(zigeeBindRequest.has_key(ord(seqNumber))):
1608 tup = zigeeBindRequest[ord(seqNumber)]
1609 zigeeBindRequestMutex.release()
1612 # cant really do anything in this case...
1613 # don't have any information on who the data is for
1616 # successful binding
1617 if(ord(statusCode) == 0):
1619 # add a callback for this specific device and cluster
1620 # to the HA callback dict
1621 zibeeHACallbackMutex.acquire();
1622 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1623 if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1624 zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1626 zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1627 zibeeHACallbackMutex.release()
1629 # send success message
1630 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1633 elif (ord(statusCode) == 170):
1634 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1637 elif (ord(statusCode) == 174):
1638 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1640 # Other issue, dont have code for
1642 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1644 # if this is a response to a short address query
1645 elif(parsedData['cluster'] == '\x80\x00'):
1646 print ">response to a short address query 0x8000"
1649 statusCode = parsedData['rf_data'][0]
1651 # does not matter if this is not a success, we can try again later
1652 if(statusCode != '\x00'):
1653 # status code was not success so do not do anything
1656 # get the short and long address information
1657 longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1658 shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1660 # remove device from list of unregistered devices if it is in it
1661 zigbeeUnregisteredAddressesMutex.acquire()
1662 if(longAddr in zigbeeUnregisteredAddresses):
1663 zigbeeUnregisteredAddresses.remove(longAddr)
1664 zigbeeUnregisteredAddressesMutex.release()
1666 # update/insert the short address
1667 zigbeeLongShortAddrMutex.acquire()
1668 zigbeeLongShortAddr[longAddr] = shortAddr
1669 zigbeeLongShortAddrMutex.release()
1672 elif(parsedData['cluster'] == '\x80\x06'):
1674 print '[ 0x8006 ] get Match Descriptor Response'
1675 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1678 elif(parsedData['cluster'] == '\x80\x36'):
1680 print '[ 0x8036 ] get Management Permit Joining Response'
1681 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1683 ManagementPermitJoiningReqSuccess = True
1688 print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1689 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1692 # if this is a home automation zcl message/response
1693 elif (parsedData['profile'] == '\x01\x04'):
1695 # get the zcl message header
1696 zclFrameControl = parsedData['rf_data'][0]
1697 zclSeqNumber = parsedData['rf_data'][1]
1698 zclCommand = parsedData['rf_data'][2]
1699 zclStatus = parsedData['rf_data'][3]
1702 if(zclCommand == '\x00'):
1704 print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1706 # find who to send response
1708 zigbeeSeqNumberToClientMutex.acquire()
1710 if(longAddr in seqNumberForNotification):
1712 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1713 tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1714 #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1715 zigbeeSeqNumberToClientMutex.release()
1717 # no one to send the response to so just move on
1719 # cant really do anything here
1721 # create the response message
1723 message = "type : zcl_zone_status_change_notification\n"
1724 message += "packet_id: " + packetId + "\n"
1725 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1726 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1727 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1728 message += "attributes: success"
1731 sendSoceket.sendto(message,tup[0])
1732 print(">port : ", tup[0][1])
1735 #this is change on/off response
1736 elif(parsedData['cluster'] == '\x00\x06'):
1739 # find who to send response to
1741 zigbeeSeqNumberToClientMutex.acquire()
1742 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1743 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1744 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1745 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1746 zigbeeSeqNumberToClientMutex.release()
1747 # no one to send the response to so just move on
1749 # cant really do anything here
1751 # create the response message
1755 message = "type : zcl_change_switch_response\n"
1756 message += "packet_id: " + packetId + "\n"
1757 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1758 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1759 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1760 message += "attributes: success"
1763 sendSoceket.sendto(message,tup[0])
1765 # this is a zcl read attribute response
1766 elif(zclCommand == '\x01'):
1768 # get the zcl payload
1769 zclPayload = parsedData['rf_data'][3:]
1770 attibuteResponseList = []
1772 # get the data for each data
1773 while(len(zclPayload) > 0):
1774 attributeId = zclPayload[0:2]
1775 attributeStatus = zclPayload[2]
1776 zclPayload = zclPayload[3:]
1778 if(ord(attributeStatus) != 0):
1779 # if attribute is not supported then it has no data
1780 # package the data and add it to the list
1781 attibuteResponseList.append((attributeId,"not_supported"))
1784 # get the data type and data length of the attributre
1785 attributeType = zclPayload[0]
1786 dataLength = zclDataTypeToBytes(zclPayload)
1788 # consume zcl payload data
1789 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1790 zclPayload = zclPayload[2:]
1791 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1792 zclPayload = zclPayload[3:]
1794 zclPayload = zclPayload[1:]
1796 # package the data and add it to the list
1797 newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1798 attibuteResponseList.append(newData)
1800 # consume the data size of the payload
1801 zclPayload = zclPayload[dataLength:]
1803 # find who to send response to
1805 zigbeeSeqNumberToClientMutex.acquire()
1806 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1807 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1808 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1809 zigbeeSeqNumberToClientMutex.release()
1811 # no one to send the response to so just move on
1813 # cant really do anything here
1816 # create the response message
1818 message = "type : zcl_read_attributes_response \n"
1819 message += "packet_id: " + packetId + "\n"
1820 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1821 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1822 message += "attributes: "
1824 # create the message for each attribute
1825 for t in attibuteResponseList:
1826 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1827 if(t[1] == "success"):
1828 attrIdStr = "%0.4x" % attrId
1829 attrIdStr = changeEndian(attrIdStr)
1831 message += attrIdStr
1833 message += "success"
1835 message += "%0.2x" % ord(t[2])
1840 dat += "%0.2x" % ord(c)
1841 dat = changeEndian(dat)
1845 attrIdStr = "%0.4x" % attrId
1846 attrIdStr = changeEndian(attrIdStr)
1848 message += attrIdStr
1850 message += "not_supported"
1853 message = message[0:len(message) - 1]
1856 sendSoceket.sendto(message,tup[0])
1862 # this is a zcl write attribute response
1863 elif(zclCommand == '\x04'):
1865 # get the zcl payload
1866 zclPayload = parsedData['rf_data'][3]
1867 # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1868 if(zclStatus == '\x70' or zclPayload == '\x00'):
1870 # find who to send response to
1872 zigbeeSeqNumberToClientMutex.acquire()
1873 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1874 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1875 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1876 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1877 zigbeeSeqNumberToClientMutex.release()
1878 # no one to send the response to so just move on
1880 # cant really do anything here
1883 # create the response message
1885 message = "type : zcl_write_attributes_response\n"
1886 message += "packet_id: " + packetId + "\n"
1887 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1888 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1889 message += "attributes: success"
1892 sendSoceket.sendto(message,tup[0])
1894 print '[ 0x0500 ] get Write Attribute Response success'
1895 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1899 print '[ 0x0500 ] get Write Attribute Response'
1900 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1904 # this is a zcl configure attribute response
1905 elif(zclCommand == '\x07'):
1907 # find who to send response to
1909 zigbeeSeqNumberToClientMutex.acquire()
1910 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1911 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1912 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1913 zigbeeSeqNumberToClientMutex.release()
1915 # no one to send the response to so just move on
1917 # cant really do anything here
1921 zclPayload = parsedData['rf_data'][3:]
1923 # construct the message
1925 message = "type : zcl_configure_reporting_response \n"
1926 message += "packet_id: " + packetId + "\n"
1927 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1928 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1929 message += "attributes: "
1931 if(len(zclPayload) == 1):
1932 # if all the configurations are a success then only send back a success
1933 # based on zigbee specs
1934 message += "all_success \n";
1935 sendSoceket.sendto(message,tup[0])
1938 attibuteResponseList = []
1940 # get each attributes data
1941 while(len(zclPayload) > 0):
1942 attributeStatus = zclPayload[0]
1943 attributeDirection = zclPayload[1]
1944 attributeId = zclPayload[2:4]
1945 zclPayload = zclPayload[4:]
1947 newData = (attributeStatus,attributeDirection, attributeId)
1948 attibuteResponseList.append(newData)
1950 # package each attribute
1951 for t in attibuteResponseList:
1952 attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1953 attrIdStr = "%0.4x" % attrId
1954 attrIdStr = changeEndian(attrIdStr)
1956 message += attrIdStr
1959 message += "success"
1966 message += "reported"
1968 message += "received"
1971 message = message[0:len(message) - 1]
1973 sendSoceket.sendto(message,tup[0])
1975 # this is a zcl report attribute message
1976 #elif(zclCommand == '\x0a'):
1977 elif(parsedData['cluster'] == '\x0B\x04'):
1978 print "get Report attribute "
1979 # get teh zcl payload
1980 zclPayload = parsedData['rf_data'][3:]
1981 attibuteResponseList = []
1983 # extract the attribute data
1984 while(len(zclPayload) > 0):
1985 attributeId = zclPayload[0:2]
1986 zclPayload = zclPayload[2:]
1987 attributeType = zclPayload[0]
1988 dataLength = zclDataTypeToBytes(zclPayload)
1990 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1991 zclPayload = zclPayload[2:]
1992 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1993 zclPayload = zclPayload[3:]
1995 zclPayload = zclPayload[1:]
1997 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1998 attibuteResponseList.append(newData)
1999 zclPayload = zclPayload[dataLength:]
2002 # get callback clients to respond to
2003 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
2005 zibeeHACallbackMutex.acquire()
2006 if(zibeeHACallback.has_key(callbackIndex)):
2007 retAddr = zibeeHACallback[callbackIndex]
2008 zibeeHACallbackMutex.release()
2010 # no one to respond to so do nothing here
2011 if(retAddr == None):
2014 # construct the message
2015 message = "type : zcl_report_attributes \n"
2016 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
2017 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
2018 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
2019 message += "attributes: "
2021 # package the attributes
2022 for t in attibuteResponseList:
2023 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
2024 attrIdStr = "%0.4x" % attrId
2025 attrIdStr = changeEndian(attrIdStr)
2027 message += attrIdStr
2029 message += "%0.2x" % ord(t[1])
2034 dat += "%0.2x" % ord(c)
2035 dat = changeEndian(dat)
2039 message = message[0:len(message) - 1]
2041 print "Sending", message
2043 # send to all client that want this callback
2045 sendSoceket.sendto(message,ra)
2047 # -----------------------------------------------------------------------------
2048 # Communication Callback/Parse Methods
2049 # -----------------------------------------------------------------------------
2050 def handleNewZigbeeMessage(parsedData):
2051 ''' Method to process a zigbee message from the local radio.
2053 parsedData -- Pre-parsed (into a dict) data from message.
2055 #print "=================================================================="
2057 print "New Zigbee Message"
2058 #printMessageData(parsedData)
2060 # dispatch to the correct zigbee handler
2061 if (parsedData['id'] == 'at_response'):
2062 print "parsedDataID : at_response"
2063 processZigbeeATCommandMessage(parsedData)
2065 elif (parsedData['id'] == 'rx_explicit'):
2066 print "parsedDataID : rx_explicit"
2067 processZigbeeRxExplicitCommandMessage(parsedData)
2070 print "Unknown API format"
2072 #print "=================================================================="
2074 def handleNewUdpPacket(data, addr):
2075 ''' Method to parse and handle an incoming UDP packet.
2077 data -- Data that was in the UDP packet.
2078 addr -- Address (IP and Port) of the UDP packet origin.
2080 global ManagementPermitJoiningReqSuccess
2082 #print "=================================================================="
2084 #print "Got New UDP packet..."
2088 # data comes in as 'key: value\n key: value...' string and so needs to be
2089 # parsed into a dict
2092 # 1 key, value pair per line
2093 for line in data.split('\n'):
2095 # key and values are split based on a ':'
2096 fields = line.split(':')
2098 # make sure properly formated otherwise just ignore it
2099 if len(fields) == 2:
2101 # do strips to remove any white spacing that may have resulted
2102 # from improper packing on the sender side
2103 parsedData[fields[0].strip()] = fields[1].strip()
2106 # wrap in try statement just in case there is an improperly formated packet we
2109 # dispatch to the correct process method
2110 if(parsedData["type"] == "zdo_bind_request"):
2111 print "> processUdpZdoBindReqMessage call"
2112 processUdpZdoBindReqMessage(parsedData, addr)
2113 elif(parsedData["type"] == "zdo_unbind_request"):
2114 processUdpZdoUnBindReqMessage(parsedData, addr)
2115 elif(parsedData["type"] == "send_address"):
2116 print "> processUdpSendAddressMessage call"
2117 processUdpSendAddressMessage(parsedData, addr)
2118 elif(parsedData["type"] == "zcl_read_attributes"):
2119 processUdpZclReadAttributesMessage(parsedData, addr)
2120 elif(parsedData["type"] == "zcl_configure_reporting"):
2121 print "> zcl_configure_reporting call"
2122 processUdpZclConfigureReportingMessage(parsedData, addr)
2123 elif(parsedData["type"] == "policy_set"):
2124 processUdpPolicySet(parsedData, addr)
2125 elif(parsedData["type"] == "policy_clear"):
2126 processUdpPolicyClear(parsedData, addr)
2127 elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo
2128 processUdpManagementPermitJoiningReqMessage(parsedData, addr)
2129 elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo
2130 processUdpZclWriteAttributesMessage(parsedData, addr)
2131 elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo
2132 processUdpEnrollmentResponse(parsedData, addr)
2133 elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo
2134 processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
2135 elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo
2136 processUdpZclChangeSwitchReqMessage(parsedData, addr)
2137 elif(parsedData["type"] == "zcl_lock_or_unlock_door_request"): #made by Jiawei
2138 processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr)
2139 elif(parsedData["type"] == "zcl_read_door_status_request"): #made by Jiawei
2140 processUdpZclReadDoorStatusReqMessage(parsedData, addr)
2142 print "unknown Packet: " + parsedData["type"]
2145 # if we ever get here then something went wrong and so just ignore this
2146 # packet and try again later
2147 print "I didn't expect this error:", sys.exc_info()[0]
2148 traceback.print_exc()
2150 #print "=================================================================="
2153 # -----------------------------------------------------------------------------
2154 # Main Running Methods
2155 # -----------------------------------------------------------------------------
2158 '''Main function used for starting the application as the main driver'''
2160 global ZIGBEE_SERIAL_PORT
2161 global ZIGBEE_SERIAL_BAUD
2162 global UDP_RECEIVE_PORT
2163 global zigbeeConnection
2167 parseCommandLineArgs(sys.argv[1:])
2169 # create serial object used for communication to the zigbee radio
2170 sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
2172 # create a zigbee object that handles all zigbee communication
2173 # we use this to do all communication to and from the radio
2174 # when data comes from the radio it will get a bit of unpacking
2175 # and then a call to the callback specified will be done with the
2177 zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
2179 # get the long address of our local radio before we start doing anything
2180 getConnectedRadioLongAddress();
2182 # setup incoming UDP socket and bind it to self and specified UDP port
2183 # sending socket does not need to be bound to anything
2184 #receiveSoceket.bind(('192.168.2.227', UDP_RECEIVE_PORT))
2185 receiveSoceket.bind(('127.0.0.1', UDP_RECEIVE_PORT))
2187 # create the thread that does short address lookups
2188 addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
2189 addressUpdateWorkerThread.start()
2194 print "=================================================================="
2197 print "=================================================================="
2199 # wait for an incoming UDP packet
2200 # this is a blocking call
2201 data, addr = receiveSoceket.recvfrom(4096)
2203 # handle the UDP packet appropriately
2204 handleNewUdpPacket(data, addr)
2206 except KeyboardInterrupt:
2207 # use the keyboard interupt to catch a ctrl-c and kill the application
2211 # something went really wrong and so exit with error message
2212 traceback.print_exc()
2214 # signal all threads to exit
2217 # wait for threads to finish before closing of the resources
2218 addressUpdateWorkerThread.join()
2221 # make sure to close all the connections
2222 zigbeeConnection.halt()
2223 receiveSoceket.close()
2226 if __name__ == "__main__":
2227 # call main function since this is being run as the start