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.1.198", 12345) # ip address and portof the system master node
20 #SYSTEM_MASTER_ADDRESS = ("192.168.2.108", 22222) # ip address and portof the system master node
21 #SYSTEM_MASTER_ADDRESS2 = ("192.168.2.108", 11111)
22 #SYSTEM_MASTER_ADDRESS3 = ("192.168.2.108", 11222)
24 # time for messages to wait for a response before the system clears away that
26 ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5
28 #ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH" # USB-Serial port of local radio
29 ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0"
30 ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port
32 # address of our local zigbee radio
33 #ZIGBEE_DEVICE_ADDRESS = "0013a20040d99cb4"
34 ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx"
36 # -----------------------------------------------------------------------------
37 # Global Variables and Objects
38 # -----------------------------------------------------------------------------
40 # signals to see if a request needs to be made
41 didGetLocalRadioHighAddress = False;
42 didGetLocalRadioLowAddress = False;
44 # zigbee communications object and its mutex
45 zigbeeConnection = None
46 zigbeeConnectionMutex = Lock()
48 #singleton mabe by changwoo
49 matchDescriptorReqSingleton = True
50 deviceAnnouncementSingleton = True
51 ManagementPermitJoiningReqSuccess = False
53 # zigbee mapping from long to short object dict
54 zigbeeLongShortAddr = dict()
55 zigbeeLongShortAddrMutex = Lock()
57 # zigbee mapping from a sequence number to a client
58 # for correct response handling
59 zigbeeSeqNumberToClient = dict()
60 zigbeeSeqNumberToClientMutex = Lock()
62 zigeeBindRequest = dict()
63 zigeeBindRequestMutex = Lock()
65 # Keeps record of where to send callbacks to when an HA message is received
66 zibeeHACallback = dict()
67 zibeeHACallbackMutex = Lock()
70 # Keeps a record of device addresses whose short addresses have not been
72 zigbeeUnregisteredAddresses = []
73 zigbeeUnregisteredAddressesMutex = Lock()
75 # used to signal all threads to end
79 # 2 sockets, one for sending (not bound to a port manually)
80 # and one for receiving, known port binding by application
82 sendSoceket = socket(AF_INET, SOCK_DGRAM)
83 receiveSoceket = socket(AF_INET, SOCK_DGRAM)
86 # zigbee address authority list
87 zigbeeAddressAuthorityDict = dict()
90 seqNumberForNotification = dict()
92 # -----------------------------------------------------------------------------
94 # -----------------------------------------------------------------------------
95 def reverseShortAddress(shortAddr):
96 result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2]
99 def parseCommandLineArgs(argv):
100 global ZIGBEE_SERIAL_PORT
101 global ZIGBEE_SERIAL_BAUD
103 opts, args = getopt.getopt(
104 argv, "hp:b:u:", ["port=", "baud=", "udpport="])
106 except getopt.GetoptError:
107 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
110 for opt, arg in opts:
112 print 'test.py -p <serial_port> -b <baud_rate> -u <udp_port>'
115 elif opt in ("-p", "--port"):
116 ZIGBEE_SERIAL_PORT = arg
118 elif opt in ("-b", "--baud"):
120 ZIGBEE_SERIAL_BAUD = int(arg)
122 print "Buad rate must be an integer"
127 # Convenience (Stateless)
130 def hexListToChar(hexList):
131 ''' Method to convert a list/string of characters into their corresponding values
133 hexList -- list or string of hex characters
137 retString += chr(int(h, 16))
140 def splitByN(seq, n):
141 ''' Method to split a string into groups of n characters
146 return [seq[i:i+n] for i in range(0, len(seq), n)]
148 def changeEndian(hexString):
149 ''' Method to change endian of a hex string
151 hexList -- string of hex characters
153 split = splitByN(hexString, 2) # get each byte
154 split.reverse(); # reverse ordering of the bytes
162 def printMessageData(data):
163 ''' Method to print a zigbee message to the console
165 data -- pre-parsed zigbee message
170 print "{0:02x}".format(ord(e)),
172 print "({})".format(data[d]),
175 def hexStringToZigbeeHexString(hexString):
176 ''' Method to change a hex string to a string of characters with the hex values
178 hexList -- string of hex characters
180 return hexListToChar(splitByN(hexString, 2))
182 def zigbeeHexStringToHexString(zigbeeHexString):
183 ''' Method to change string of characters with the hex values to a hex string
185 hexList -- string of characters with hex values
189 for e in zigbeeHexString:
190 retString += "{0:02x}".format(ord(e))
193 def zclDataTypeToBytes(zclPayload):
194 ''' Method to determine data length of a zcl attribute
196 zclPayload -- ZCL payload data, must have dataType as first byte
198 attrType = ord(zclPayload[0])
200 if(attrType == 0x00):
202 elif (attrType == 0x08):
204 elif (attrType == 0x09):
206 elif (attrType == 0x0a):
208 elif (attrType == 0x0b):
210 elif (attrType == 0x0c):
212 elif (attrType == 0x0d):
214 elif (attrType == 0x0e):
216 elif (attrType == 0x0f):
218 elif (attrType == 0x10):
220 elif (attrType == 0x18):
222 elif (attrType == 0x19):
224 elif (attrType == 0x1a):
226 elif (attrType == 0x1b):
228 elif (attrType == 0x1c):
230 elif (attrType == 0x1d):
232 elif (attrType == 0x1e):
234 elif (attrType == 0x1f):
236 elif (attrType == 0x20):
238 elif (attrType == 0x21):
240 elif (attrType == 0x22):
242 elif (attrType == 0x23):
244 elif (attrType == 0x24):
246 elif (attrType == 0x25):
248 elif (attrType == 0x26):
250 elif (attrType == 0x27):
252 elif (attrType == 0x28):
254 elif (attrType == 0x29):
256 elif (attrType == 0x2a):
258 elif (attrType == 0x2b):
260 elif (attrType == 0x2c):
262 elif (attrType == 0x2d):
264 elif (attrType == 0x2e):
266 elif (attrType == 0x2f):
268 elif (attrType == 0x30):
270 elif (attrType == 0x31):
272 elif (attrType == 0x38):
274 elif (attrType == 0x39):
276 elif (attrType == 0x3a):
278 elif (attrType == 0x41):
279 return ord(zclPayload[1])
280 elif (attrType == 0x42):
281 return ord(zclPayload[1])
282 elif (attrType == 0x43):
283 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
284 elif (attrType == 0x44):
285 return ord(zclPayload[1]) + (256 * ord(zclPayload[2]))
286 elif (attrType == 0xe0):
288 elif (attrType == 0xe1):
290 elif (attrType == 0xe2):
292 elif (attrType == 0xe8):
294 elif (attrType == 0xe9):
296 elif (attrType == 0xea):
298 elif (attrType == 0xf0):
300 elif (attrType == 0xf1):
302 elif (attrType == 0xff):
309 def createSequenceNumberForClient(addr, packetId):
310 ''' Method to get and store a sequence number with a specific client
311 for a specific message.
313 addr -- UDP address of the client to associate with the seq. number
314 packetId -- packet id from the UDP packet
316 # keep trying to find a number to return
319 # get the current time
320 epoch_time = int(time.time())
322 # get the current list of used numbers
323 zigbeeSeqNumberToClientMutex.acquire()
324 keysList = zigbeeSeqNumberToClient.keys()
325 zigbeeSeqNumberToClientMutex.release()
327 # if all the numbers are already used
328 if(len(keysList) == 256):
330 # get a list of all the items
331 zigbeeSeqNumberToClientMutex.acquire()
332 itemsList = zigbeeSeqNumberToClient.items()
333 zigbeeSeqNumberToClientMutex.release()
335 # search for a number that is old enough to get rid of otherwise use -1
337 for item in itemsList:
338 if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC):
343 # replace the record with new data if we found one to replace
344 zigbeeSeqNumberToClientMutex.acquire()
345 zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId)
346 zigbeeSeqNumberToClientMutex.release()
351 # not all numbers used yet so pick one randomly
352 randNum = random.randrange(0,256)
354 # check if we are using the number yet
355 if(randNum not in keysList):
357 # we are not so insert to keep track who this number is for and return it
358 zigbeeSeqNumberToClientMutex.acquire()
359 zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId)
360 zigbeeSeqNumberToClientMutex.release()
363 def getConnectedRadioLongAddress():
364 """ Method to make sure we get the MAC address of our local radio"""
365 global zigbeeConnection
368 # keep looping until we get both the MSBs and the LSBs
369 while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)):
372 zigbeeConnection.send('at', command="SH")
373 zigbeeConnection.send('at', command="SL")
375 # sleep for a bit to give the radio time to respond before we check again
379 def addressUpdateWorkerMethod():
380 ''' Method to keep refreshing the short addresses of the known zigbee devices'''
382 global zigbeeLongShortAddr
383 global zigbeeLongShortAddrMutex
384 global zigbeeUnregisteredAddresses
385 global zigbeeUnregisteredAddressesMutex
386 global zigbeeConnectionMutex
387 global zigbeeConnection
389 # keep looping until signaled to quit
390 while(not doEndFlag):
394 # add unregistered (short addresses unknown) devices so
395 # that we can look them up
396 zigbeeUnregisteredAddressesMutex.acquire()
397 addrList.extend(zigbeeUnregisteredAddresses)
398 zigbeeUnregisteredAddressesMutex.release()
400 # add the devices that we have short addresses for so we can
401 # get their most recent short addresses
402 zigbeeLongShortAddrMutex.acquire()
403 addrList.extend(zigbeeLongShortAddr.keys())
404 zigbeeLongShortAddrMutex.release()
406 # Loop through all the addresses and send messages for each address
409 # create payload for a query on the network for a short address
411 payload += hexStringToZigbeeHexString(changeEndian(ad))
414 # create and send binding command
415 zigbeeConnectionMutex.acquire()
417 zigbeeConnection.send('tx_explicit',
419 dest_addr_long=hexStringToZigbeeHexString(ad),
420 dest_addr='\xff\xfd',
422 dest_endpoint='\x00',
427 zigbeeConnectionMutex.release()
437 def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None):
438 ''' Method to send a success or fail back to a client.
440 addr -- UDP address to send packet to
441 packetTypeStr -- name of this specific packet
442 packetIdStr -- packet id to send
443 sucOrFail -- whether this is a success or fail message (True = success)
444 reason -- reason of failure (if needed, default is None)
450 # construct the message
451 message = "type: " + packetTypeStr.strip() + "\n"
452 message += "packet_id: " + packetIdStr + "\n"
455 message += "response: success \n"
457 message += "response: fail \n"
458 message += "reason: " + reason + "\n"
460 # send message in a UDP packet
461 sendSoceket.sendto(message,addr)
463 def processUdpZdoBindReqMessage(parsedData, addr):
467 if(zigbeeAddressAuthorityDict.has_key(addr)):
468 l = zigbeeAddressAuthorityDict[addr]
469 if(parsedData['device_address_long'] not in l):
474 # get the short address for this device long address if possible
475 zigbeeLongShortAddrMutex.acquire()
476 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
477 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
478 zigbeeLongShortAddrMutex.release()
480 # if there is a short address than we can send the message
481 # if there is not one then we cannot since we need both the short and
483 if(shortAddr != None):
485 # get a request number
486 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
491 # send an error message, could not get a sequence number to use at this time
492 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space')
495 # a bind request was made so must store and wait for response
496 # before we setup callbacks, so keep just the data we need to create the callback
497 zigeeBindRequestMutex.acquire()
498 zigeeBindRequest[seqNumber] = (parsedData['device_address_long'],
499 parsedData['cluster_id'],
500 parsedData['packet_id'],
502 zigeeBindRequestMutex.release()
504 # construct the short and long addresses of the message for sending
505 # make sure they are in the correct format
506 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
507 destShortAddr = hexStringToZigbeeHexString(shortAddr)
509 # create the payload data
511 payloadData += chr(seqNumber)
512 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long']))
513 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint']))
514 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id']))
515 payloadData += '\x03'
516 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
517 payloadData += '\x00'
519 # create and send binding command
520 zigbeeConnectionMutex.acquire()
521 zigbeeConnection.send('tx_explicit',
523 # frame_id=chr(seqNumber),
524 dest_addr_long=destLongAddr,
525 dest_addr=destShortAddr,
527 dest_endpoint='\x00',
532 zigbeeConnectionMutex.release()
536 # send a failure packet since there is no short address available
537 sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown')
540 def processUdpZdoUnBindReqMessage(parsedData, addr):
541 zibeeHACallbackMutex.acquire();
542 if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])):
543 zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr)
544 zibeeHACallbackMutex.release()
545 sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True)
549 def processUdpSendAddressMessage(parsedData, addr):
550 ''' Method handle a send address command
552 parsedData -- Pre-parsed Data that was in the UDP packet.
553 addr -- Address (IP and Port) of the UDP packet origin.
555 global zigbeeLongShortAddr
556 global zigbeeLongShortAddrMutex
557 global zigbeeUnregisteredAddresses
558 global zigbeeUnregisteredAddressesMutex
561 print "process send address"
564 # construct success message
565 message = "type: send_address_response\n"
566 message += "packet_id: " + parsedData['packet_id'] + "\n"
567 message += "response: success\n"
569 # tell client that we got their request
570 sendSoceket.sendto(message,addr)
571 print "responding", message
574 zigbeeLongShortAddrMutex.acquire()
575 doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long'])
576 zigbeeLongShortAddrMutex.release()
579 # long address is already registered with the system so no need to do anything
582 # long address not registered so add it for short address lookup
583 zigbeeUnregisteredAddressesMutex.acquire()
584 zigbeeUnregisteredAddresses.append(parsedData['device_address_long'])
585 zigbeeUnregisteredAddressesMutex.release()
590 def processUdpEnrollmentResponse(parsedData, addr):
592 global zigbeeLongShortAddr
593 global zigbeeLongShortAddrMutex
594 global zigeeBindRequestMutex
595 global zigeeBindRequest
596 global zigbeeConnectionMutex
597 global zigbeeConnection
600 # get the short address for this device long address if possible
601 zigbeeLongShortAddrMutex.acquire()
602 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
603 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
604 zigbeeLongShortAddrMutex.release()
607 # if there is a short address than we can send the message
608 # if there is not one then we cannot since we need both the short and
610 if(shortAddr != None):
612 # get a request number
613 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
618 # send an error message, could not get a sequence number to use at this time
619 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space')
622 # get the info for sending
623 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
624 destShortAddr = hexStringToZigbeeHexString(shortAddr)
625 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
626 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
627 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
629 # create the payload data
631 payloadData += '\x01'
632 payloadData += chr(seqNumber)
633 payloadData += '\x00'
634 payloadData += '\x00\x00'
636 # create and send binding command
637 zigbeeConnectionMutex.acquire()
638 zigbeeConnection.send('tx_explicit',
640 # frame_id=chr(seqNumber),
641 dest_addr_long=destLongAddr,
642 dest_addr=destShortAddr,
644 dest_endpoint=dstEndpoint,
649 print '> EnrollmentResponse is sent'
650 zigbeeConnectionMutex.release()
654 # send a fail response
655 sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown')
662 def processUdpZclWriteAttributesMessage(parsedData, addr):
664 global zigbeeLongShortAddr
665 global zigbeeLongShortAddrMutex
666 global zigeeBindRequestMutex
667 global zigeeBindRequest
668 global zigbeeConnectionMutex
669 global zigbeeConnection
672 # get the short address for this device long address if possible
673 zigbeeLongShortAddrMutex.acquire()
674 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
675 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
676 zigbeeLongShortAddrMutex.release()
678 # if there is a short address than we can send the message
679 # if there is not one then we cannot since we need both the short and
681 if(shortAddr != None):
682 # get a request number
683 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
688 # send an error message, could not get a sequence number to use at this time
689 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space')
692 # get the info for sending
693 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
694 destShortAddr = hexStringToZigbeeHexString(shortAddr)
695 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
696 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
697 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
699 # create the payload data
701 payloadData += '\x00'
702 payloadData += chr(seqNumber)
703 payloadData += '\x02'
704 payloadData += '\x10\x00'
705 payloadData += '\xF0'
706 # payloadData += '\xDA\x9A\xD9\x40\x00\xA2\x13\x00'
707 payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS))
709 zigbeeConnectionMutex.acquire()
710 zigbeeConnection.send('tx_explicit',
712 # frame_id=chr(seqNumber),
713 dest_addr_long=destLongAddr,
714 dest_addr=destShortAddr,
716 dest_endpoint=dstEndpoint,
723 print '> WriteAttributesReq is sent : '+str(shortAddr)
724 zigbeeConnectionMutex.release()
728 # send a fail response
729 sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown')
733 def processUdpZclChangeSwitchReqMessage(parsedData, addr):
735 global zigbeeLongShortAddr
736 global zigbeeLongShortAddrMutex
737 global zigeeBindRequestMutex
738 global zigeeBindRequest
739 global zigbeeConnectionMutex
740 global zigbeeConnection
743 # get the short address for this device long address if possible
744 zigbeeLongShortAddrMutex.acquire()
745 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
746 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
747 zigbeeLongShortAddrMutex.release()
750 # if there is a short address than we can send the message
751 # if there is not one then we cannot since we need both the short and
753 if(shortAddr != None):
755 # get a request number
756 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
761 # send an error message, could not get a sequence number to use at this time
762 sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space')
765 # get the info for sending
766 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
767 destShortAddr = hexStringToZigbeeHexString(shortAddr)
768 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
769 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
770 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
771 value = hexStringToZigbeeHexString(parsedData['value'])
773 # create and send binding command
774 zigbeeConnectionMutex.acquire()
776 zigbeeConnection.send('tx_explicit',
778 # frame_id=chr(seqNumber),
779 dest_addr_long=destLongAddr,
780 dest_addr=destShortAddr,
782 dest_endpoint=dstEndpoint,
785 data='\x01'+chr(seqNumber)+value
788 if parsedData['value']==1:
789 print '> The outlet sensor turned on'
791 print '> The outlet sensor turned off'
793 zigbeeConnectionMutex.release()
797 # send a fail response
798 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
803 def processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr):
805 global zigbeeLongShortAddr
806 global zigbeeLongShortAddrMutex
807 global zigeeBindRequestMutex
808 global zigeeBindRequest
809 global zigbeeConnectionMutex
810 global zigbeeConnection
813 # get the short address for this device long address if possible
814 zigbeeLongShortAddrMutex.acquire()
815 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
816 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
817 zigbeeLongShortAddrMutex.release()
820 # if there is a short address than we can send the message
821 # if there is not one then we cannot since we need both the short and
823 if(shortAddr != None):
825 # get a request number
826 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
831 # send an error message, could not get a sequence number to use at this time
832 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'out_of_space')
835 # get the info for sending
836 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
837 destShortAddr = hexStringToZigbeeHexString(shortAddr)
838 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
839 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
840 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
841 value = hexStringToZigbeeHexString(parsedData['value'])
843 # create and send binding command
844 zigbeeConnectionMutex.acquire()
846 zigbeeConnection.send('tx_explicit',
848 dest_addr_long=destLongAddr,
849 dest_addr=destShortAddr,
851 dest_endpoint=dstEndpoint,
854 data='\x01'+chr(seqNumber)+value
858 print '> The door lock is unlocked'
859 elif value == '\x00':
860 print '> The door lock is locked'
862 print '> Unknown door lock value: ' + str(value)
864 zigbeeConnectionMutex.release()
868 # send a fail response
869 sendUdpSuccessFail(addr, 'lock_or_unlock_door_request', parsedData['packet_id'], False, 'short_address_unknown')
873 def processUdpZclReadDoorStatusReqMessage(parsedData, addr):
875 global zigbeeLongShortAddr
876 global zigbeeLongShortAddrMutex
877 global zigeeBindRequestMutex
878 global zigeeBindRequest
879 global zigbeeConnectionMutex
880 global zigbeeConnection
883 # get the short address for this device long address if possible
884 zigbeeLongShortAddrMutex.acquire()
885 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
886 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
887 zigbeeLongShortAddrMutex.release()
890 # if there is a short address than we can send the message
891 # if there is not one then we cannot since we need both the short and
893 if(shortAddr != None):
895 # get a request number
896 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
901 # send an error message, could not get a sequence number to use at this time
902 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'out_of_space')
905 # get the info for sending
906 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
907 destShortAddr = hexStringToZigbeeHexString(shortAddr)
908 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
909 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
910 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
911 # framecontrol = hexStringToZigbeeHexString(parsedData['framecontrol'])
912 # commandframe = hexStringToZigbeeHexString(parsedData['commandframe'])
913 # attribute_id = hexStringToZigbeeHexString(parsedData['attribute_id'])
915 # create and send binding command
916 zigbeeConnectionMutex.acquire()
918 zigbeeConnection.send('tx_explicit',
920 dest_addr_long=destLongAddr,
921 dest_addr=destShortAddr,
923 dest_endpoint=dstEndpoint,
926 data='\x10' + chr(seqNumber) + '\x00' + '\x00\x00'
930 zigbeeConnectionMutex.release()
931 print "send read door status"
934 # send a fail response
935 sendUdpSuccessFail(addr, 'read_door_status_request', parsedData['packet_id'], False, 'short_address_unknown')
939 def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr):
941 global zigbeeLongShortAddr
942 global zigbeeLongShortAddrMutex
943 global zigeeBindRequestMutex
944 global zigeeBindRequest
945 global zigbeeConnectionMutex
946 global zigbeeConnection
949 # get the short address for this device long address if possible
950 zigbeeLongShortAddrMutex.acquire()
951 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
952 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
953 zigbeeLongShortAddrMutex.release()
956 # if there is a short address than we can send the message
957 # if there is not one then we cannot since we need both the short and
959 if(shortAddr != None):
961 # get a request number
962 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
967 # send an error message, could not get a sequence number to use at this time
968 sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space')
971 # get the info for sending
972 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
973 destShortAddr = hexStringToZigbeeHexString(shortAddr)
974 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
976 # create and send binding command
977 zigbeeConnectionMutex.acquire()
979 zigbeeConnection.send('tx_explicit',
981 # frame_id=chr(seqNumber),
982 dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff',
983 dest_addr='\xff\xfe',
985 dest_endpoint=dstEndpoint,
991 print '> BroadcastingRouteRecordReq is sent'
993 zigbeeConnectionMutex.release()
997 # send a fail response
998 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1003 def processUdpManagementPermitJoiningReqMessage(parsedData, addr):
1005 global zigbeeLongShortAddr
1006 global zigbeeLongShortAddrMutex
1007 global zigeeBindRequestMutex
1008 global zigeeBindRequest
1009 global zigbeeConnectionMutex
1010 global zigbeeConnection
1011 global matchDescriptorReqSingleton
1014 # get the short address for this device long address if possible
1015 zigbeeLongShortAddrMutex.acquire()
1016 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1017 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1018 zigbeeLongShortAddrMutex.release()
1021 # if there is a short address than we can send the message
1022 # if there is not one then we cannot since we need both the short and
1024 if(shortAddr != None):
1026 # get a request number
1027 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1030 if(seqNumber == -1):
1032 # send an error message, could not get a sequence number to use at this time
1033 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space')
1036 # get the info for sending
1037 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1038 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1039 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1041 # create the payload data
1043 payloadData += chr(seqNumber)
1044 payloadData += '\x5a'
1045 payloadData += '\x00'
1047 # create and send binding command
1048 zigbeeConnectionMutex.acquire()
1049 zigbeeConnection.send('tx_explicit',
1051 # frame_id=chr(seqNumber),
1052 dest_addr_long=destLongAddr,
1053 dest_addr=destShortAddr,
1054 src_endpoint='\x00',
1055 dest_endpoint='\x00',
1060 print '> ManagementPermitJoiningReq is sent'
1063 matchDescriptorReqSingleton= False
1064 zigbeeConnectionMutex.release()
1068 # send a fail response
1069 sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown')
1073 def processUdpZclReadAttributesMessage(parsedData, addr):
1074 ''' Method handle a ZCL read attribute command
1076 parsedData -- Pre-parsed Data that was in the UDP packet.
1077 addr -- Address (IP and Port) of the UDP packet origin.
1080 global zigbeeLongShortAddr
1081 global zigbeeLongShortAddrMutex
1082 global zigeeBindRequestMutex
1083 global zigeeBindRequest
1084 global zigbeeConnectionMutex
1085 global zigbeeConnection
1089 if(zigbeeAddressAuthorityDict.has_key(addr)):
1090 l = zigbeeAddressAuthorityDict[addr]
1091 if(parsedData['device_address_long'] not in l):
1099 # get the short address for this device long address if possible
1100 zigbeeLongShortAddrMutex.acquire()
1101 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1102 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1103 zigbeeLongShortAddrMutex.release()
1106 # if there is a short address than we can send the message
1107 # if there is not one then we cannot since we need both the short and
1109 if(shortAddr != None):
1111 # get a request number
1112 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1115 if(seqNumber == -1):
1117 # send an error message, could not get a sequence number to use at this time
1118 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space')
1121 # get the info for sending
1122 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1123 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1124 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1125 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1126 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1128 # get all the attributes
1129 attributeIds = parsedData['attribute_ids'].split(',')
1131 # create the payload data
1133 payloadData += '\x00'
1134 payloadData += chr(seqNumber)
1135 payloadData += '\x00'
1137 # make all the attributes payloads
1138 for attr in attributeIds:
1140 attr = changeEndian(attr)
1141 payloadData += hexStringToZigbeeHexString(attr)
1143 # create and send binding command
1144 zigbeeConnectionMutex.acquire()
1145 zigbeeConnection.send('tx_explicit',
1147 # frame_id=chr(seqNumber),
1148 dest_addr_long=destLongAddr,
1149 dest_addr=destShortAddr,
1150 src_endpoint='\x00',
1151 dest_endpoint=dstEndpoint,
1156 zigbeeConnectionMutex.release()
1160 # send a fail response
1161 sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown')
1164 def processUdpZclConfigureReportingMessage(parsedData, addr):
1165 ''' Method handle a zcl configure reporting message
1167 parsedData -- Pre-parsed Data that was in the UDP packet.
1168 addr -- Address (IP and Port) of the UDP packet origin.
1171 global zigbeeLongShortAddr
1172 global zigbeeLongShortAddrMutex
1173 global zigeeBindRequestMutex
1174 global zigeeBindRequest
1175 global zigbeeConnectionMutex
1176 global zigbeeConnection
1178 if(zigbeeAddressAuthorityDict.has_key(addr)):
1179 l = zigbeeAddressAuthorityDict[addr]
1180 if(parsedData['device_address_long'] not in l):
1188 # get the short address for this device long address if possible
1189 zigbeeLongShortAddrMutex.acquire()
1190 if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])):
1191 shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']]
1192 zigbeeLongShortAddrMutex.release()
1194 # if there is a short address than we can send the message
1195 # if there is not one then we cannot since we need both the short and
1197 if(shortAddr != None):
1199 # get a request number
1200 seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id'])
1203 if(seqNumber == -1):
1204 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space')
1207 destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long'])
1208 destShortAddr = hexStringToZigbeeHexString(shortAddr)
1209 profileId = hexStringToZigbeeHexString(parsedData['profile_id'])
1210 clusterId = hexStringToZigbeeHexString(parsedData['cluster_id'])
1211 dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint'])
1213 # create the payload data
1215 payloadData += '\x00'
1216 payloadData += chr(seqNumber)
1217 payloadData += '\x06'
1218 payloadData += '\x00'
1219 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id']))
1220 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type']))
1221 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval']))
1222 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval']))
1224 if(parsedData.has_key('reportable_change')):
1225 payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change']))
1228 # create and send binding command
1229 zigbeeConnectionMutex.acquire()
1230 zigbeeConnection.send('tx_explicit',
1232 # frame_id=chr(seqNumber),
1233 dest_addr_long=destLongAddr,
1234 dest_addr=destShortAddr,
1235 src_endpoint='\x00',
1236 dest_endpoint=dstEndpoint,
1241 zigbeeConnectionMutex.release()
1245 sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown')
1248 def processUdpPolicySet(parsedData, addr):
1249 ''' Method handle a policy set message
1251 parsedData -- Pre-parsed Data that was in the UDP packet.
1252 addr -- Address (IP and Port) of the UDP packet origin.
1254 print "=================================================================="
1255 print "Policy set: ", parsedData
1256 print 'addr : ', addr
1259 # do nothing if wrong source
1260 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 :
1261 if addr == SYSTEM_MASTER_ADDRESS :
1262 key = (parsedData['ip_address'], int(parsedData['port']))
1263 if (zigbeeAddressAuthorityDict.has_key(key)):
1264 zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long'])
1266 zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']]
1269 def processUdpPolicyClear(parsedData, addr):
1270 ''' Method handle a policy set message
1272 parsedData -- Pre-parsed Data that was in the UDP packet.
1273 addr -- Address (IP and Port) of the UDP packet origin.
1275 print "=================================================================="
1276 print "Clear policy: ", parsedData
1278 # do nothing if wrong source
1279 #if addr == SYSTEM_MASTER_ADDRESS or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3:
1280 if addr == SYSTEM_MASTER_ADDRESS :
1281 zigbeeAddressAuthorityDict.clear()
1288 def processZigbeeATCommandMessage(parsedData):
1289 ''' Method to process an AT zigbee message
1291 parsedData -- Pre-parsed (into a dict) data from message.
1293 global ZIGBEE_DEVICE_ADDRESS
1294 global didGetLocalRadioHighAddress
1295 global didGetLocalRadioLowAddress
1297 # command response for the high bytes of the local device long address
1298 if(parsedData['command'] == 'SH'):
1299 # convert the parameter to a string value (human readable)
1301 for e in parsedData['parameter']:
1302 value += "{0:02x}".format(ord(e))
1304 # set the correct portion of the address
1305 ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:]
1307 #signal that we got this part of the address
1308 didGetLocalRadioHighAddress = True
1310 # command response for the low bytes of the local device long address
1311 elif(parsedData['command'] == 'SL'):
1312 # convert the parameter to a string value (human readable)
1314 for e in parsedData['parameter']:
1315 value += "{0:02x}".format(ord(e))
1317 # set the correct portion of the address
1318 ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value
1320 #signal that we got this part of the address
1321 didGetLocalRadioLowAddress = True
1323 def processZigbeeRxExplicitCommandMessage(parsedData):
1324 ''' Method to process a rx-explicit zigbee message
1326 parsedData -- Pre-parsed (into a dict) data from message.
1328 global zigeeBindRequestMutex
1329 global zigeeBindRequest
1330 global zigbeeConnectionMutex
1331 global zigbeeConnection
1332 global ManagementPermitJoiningReqSuccess
1334 # get the long and short addresses from the message payload since we can
1335 # use these to update the short addresses since this short address is fresh
1336 longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long'])
1337 shortAddr = zigbeeHexStringToHexString(parsedData['source_addr'])
1339 # check if this short address is for a device that has yet to be
1341 zigbeeUnregisteredAddressesMutex.acquire()
1342 if(longAddr in zigbeeUnregisteredAddresses):
1343 zigbeeUnregisteredAddresses.remove(longAddr)
1344 zigbeeUnregisteredAddressesMutex.release()
1346 # update/ or insert the short address
1347 zigbeeLongShortAddrMutex.acquire()
1348 zigbeeLongShortAddr[longAddr] = shortAddr
1349 zigbeeLongShortAddrMutex.release()
1351 global matchDescriptorReqSingleton
1352 global deviceAnnouncementSingleton
1353 global seqNumberForNotification
1357 if (parsedData['cluster'] == '\x01\x01' and parsedData['profile'] == '\x01\x04'):
1358 zclSeqNumber = parsedData['rf_data'][1]
1360 zigbeeSeqNumberToClientMutex.acquire()
1361 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1362 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1363 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1364 zigbeeSeqNumberToClientMutex.release()
1366 rfdata = parsedData['rf_data']
1367 framecontrol = rfdata[0]
1373 if framecontrol == '\x19':
1374 if(command == '\x00'):
1376 print "( 0x0101 ) Door Lock: Lock Door Response"
1377 print time.strftime("%H:%M:%S", time.localtime())
1379 if(value == '\x00'):
1380 print "Door locked successfully"
1382 print "An error occurred in door locking"
1383 elif(command == '\x01'):
1385 print "( 0x0101 ) Door Lock: Unlock Door Response"
1386 print time.strftime("%H:%M:%S", time.localtime())
1388 if(value == '\x00'):
1389 print "Door unlocked successfully"
1391 print "An error occurred in door unlocking"
1393 elif framecontrol == '\x18':
1394 if(command == '\x01'):
1395 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1396 if attributeId == 0x0000:
1398 print "Door status: "
1400 print "Not fully locked"
1401 elif value == '\x01':
1403 elif value == '\x02':
1406 print "Unknown value: " + zigbeeHexStringToHexString(value)
1408 message = "type : zcl_read_attributes_response \n"
1409 message += "packet_id: " + packetId + "\n"
1410 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1411 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1412 message += "attributes: "
1414 attrIdStr = "%0.4x" % attributeId
1415 attrIdStr = changeEndian(attrIdStr)
1416 message += attrIdStr
1419 zclPayload = parsedData['rf_data'][3:]
1420 zclPayload = zclPayload[3:]
1421 attributeType = zclPayload[0]
1422 message += "%0.2x" % ord(attributeType)
1425 message += "success"
1428 message += "%0.2x" % ord(value)
1433 message = message[0:len(message) - 1]
1436 # no one to send the response to so just move on
1438 # cant really do anything here
1440 sendSoceket.sendto(message,tup[0])
1441 elif command == '\x07':
1444 print "( 0x0101 ) Door Lock: Configure reporting response"
1445 print 'rfdata : ' + zigbeeHexStringToHexString(rfdata)
1446 if status == '\x00':
1447 print "Configure report successfully"
1448 message = "type : zcl_configure_reporting_response \n"
1449 message += "packet_id: " + packetId + "\n"
1450 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1451 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1452 message += "attributes: "
1453 message += "all_success \n";
1455 # no one to send the response to so just move on
1457 # cant really do anything here
1459 sendSoceket.sendto(message,tup[0])
1461 print "Configure report unsuccessfully, status =", zigbeeHexStringToHexString(status)
1462 elif(command == '\x0A'):
1464 attributeId = (ord(rfdata[3]) * 256) + ord(rfdata[4])
1465 if attributeId == 0x0000:
1468 print "Not fully locked"
1469 elif value == '\x01':
1471 elif value == '\x02':
1474 print "Unknown value: " + zigbeeHexStringToHexString(value)
1476 message = "type : zcl_read_attributes_response \n"
1477 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1478 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1479 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1480 message += "attributes: "
1482 attrIdStr = "%0.4x" % attributeId
1483 attrIdStr = changeEndian(attrIdStr)
1484 message += attrIdStr
1487 zclPayload = parsedData['rf_data'][3:]
1488 zclPayload = zclPayload[3:]
1489 attributeType = zclPayload[0]
1490 message += "%0.2x" % ord(attributeType)
1493 message += "success"
1496 message += "%0.2x" % ord(value)
1501 message = message[0:len(message) - 1]
1504 # get callback clients to respond to
1505 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1507 zibeeHACallbackMutex.acquire()
1508 if(zibeeHACallback.has_key(callbackIndex)):
1509 retAddr = zibeeHACallback[callbackIndex]
1510 zibeeHACallbackMutex.release()
1512 # no one to respond to so do nothing here
1513 if(retAddr == None):
1516 sendSoceket.sendto(message,ra)
1519 # if this is a ZDO message/response
1520 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1522 #print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
1523 if(parsedData['profile'] == '\x00\x00'):
1526 # if this is a Match Descriptor Request so we need to answer.
1527 if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton):
1528 zigbeeConnectionMutex.acquire()
1529 zigbeeConnection.send('tx_explicit',
1531 # frame_id=chr(seqNumber),
1532 dest_addr_long=parsedData['source_addr_long'],
1533 dest_addr=parsedData['source_addr'],
1534 src_endpoint='\x00',
1535 dest_endpoint='\x00',
1538 data=parsedData['rf_data']
1541 zigbeeConnection.send('tx_explicit',
1543 # frame_id=chr(seqNumber),
1544 dest_addr_long=parsedData['source_addr_long'],
1545 dest_addr=parsedData['source_addr'],
1546 src_endpoint='\x00',
1547 dest_endpoint='\x00',
1550 data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01'
1554 print '[ 0x0006 ] Match Descriptor Request - answered'
1555 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1556 zigbeeConnectionMutex.release()
1559 # if this is a device announcement so we can get some useful data from it
1560 elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton):
1562 # pick out the correct parts of the payload
1563 longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11])
1564 shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3])
1566 # change the endian of the address
1567 longAddr = changeEndian(longAddr)
1568 shortAddr = changeEndian(shortAddr)
1570 # update the table with the new information
1571 zigbeeLongShortAddrMutex.acquire()
1572 zigbeeLongShortAddr[longAddr] = shortAddr
1573 zigbeeLongShortAddrMutex.release()
1575 # check if this short address is for a device that has yet to be
1577 zigbeeUnregisteredAddressesMutex.acquire()
1578 if(longAddr in zigbeeUnregisteredAddresses):
1579 zigbeeUnregisteredAddresses.remove(longAddr)
1580 zigbeeUnregisteredAddressesMutex.release()
1584 zigbeeConnectionMutex.acquire()
1585 zigbeeConnection.send('tx_explicit',
1587 # frame_id=chr(seqNumber),
1588 dest_addr_long=parsedData['source_addr_long'],
1589 dest_addr=parsedData['source_addr'],
1590 src_endpoint='\x00',
1591 dest_endpoint='\x00',
1594 data=parsedData['rf_data']
1597 print '[ 0x0013 ] device announcement - answered'
1598 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1599 deviceAnnouncementSingleton = False
1600 zigbeeConnectionMutex.release()
1603 # if this is a response to a zdo bind_req message
1604 elif(parsedData['cluster'] == '\x80\x21'):
1606 # get the status and sequence number from the message
1607 seqNumber = parsedData['rf_data'][0]
1608 statusCode = parsedData['rf_data'][1]
1609 print ">response to a zdo bind_req message parsedData>"
1611 # get the bind tuple information
1612 # for this specific bind request
1614 zigeeBindRequestMutex.acquire()
1615 if(zigeeBindRequest.has_key(ord(seqNumber))):
1616 tup = zigeeBindRequest[ord(seqNumber)]
1617 zigeeBindRequestMutex.release()
1620 # cant really do anything in this case...
1621 # don't have any information on who the data is for
1624 # successful binding
1625 if(ord(statusCode) == 0):
1627 # add a callback for this specific device and cluster
1628 # to the HA callback dict
1629 zibeeHACallbackMutex.acquire();
1630 if(zibeeHACallback.has_key((tup[0], tup[1]))):
1631 if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]):
1632 zibeeHACallback[(tup[0], tup[1])].append(tup[3])
1634 zibeeHACallback[(tup[0], tup[1])] = [tup[3]]
1635 zibeeHACallbackMutex.release()
1637 # send success message
1638 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True)
1641 elif (ord(statusCode) == 170):
1642 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported')
1645 elif (ord(statusCode) == 174):
1646 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full')
1648 # Other issue, dont have code for
1650 sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other')
1652 # if this is a response to a short address query
1653 elif(parsedData['cluster'] == '\x80\x00'):
1654 print ">response to a short address query 0x8000"
1657 statusCode = parsedData['rf_data'][0]
1659 # does not matter if this is not a success, we can try again later
1660 if(statusCode != '\x00'):
1661 # status code was not success so do not do anything
1664 # get the short and long address information
1665 longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10]))
1666 shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12]))
1668 # remove device from list of unregistered devices if it is in it
1669 zigbeeUnregisteredAddressesMutex.acquire()
1670 if(longAddr in zigbeeUnregisteredAddresses):
1671 zigbeeUnregisteredAddresses.remove(longAddr)
1672 zigbeeUnregisteredAddressesMutex.release()
1674 # update/insert the short address
1675 zigbeeLongShortAddrMutex.acquire()
1676 zigbeeLongShortAddr[longAddr] = shortAddr
1677 zigbeeLongShortAddrMutex.release()
1680 elif(parsedData['cluster'] == '\x80\x06'):
1682 print '[ 0x8006 ] get Match Descriptor Response'
1683 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1686 elif(parsedData['cluster'] == '\x80\x36'):
1688 print '[ 0x8036 ] get Management Permit Joining Response'
1689 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1691 ManagementPermitJoiningReqSuccess = True
1696 print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...'
1697 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1700 # if this is a home automation zcl message/response
1701 elif (parsedData['profile'] == '\x01\x04'):
1703 # get the zcl message header
1704 zclFrameControl = parsedData['rf_data'][0]
1705 zclSeqNumber = parsedData['rf_data'][1]
1706 zclCommand = parsedData['rf_data'][2]
1707 zclStatus = parsedData['rf_data'][3]
1710 if(zclCommand == '\x00'):
1712 print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] )
1714 # find who to send response
1716 zigbeeSeqNumberToClientMutex.acquire()
1718 if(longAddr in seqNumberForNotification):
1720 if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])):
1721 tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]]
1722 #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete.
1723 zigbeeSeqNumberToClientMutex.release()
1725 # no one to send the response to so just move on
1727 # cant really do anything here
1729 # create the response message
1731 message = "type : zcl_zone_status_change_notification\n"
1732 message += "packet_id: " + packetId + "\n"
1733 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1734 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1735 message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n"
1736 message += "attributes: success"
1739 sendSoceket.sendto(message,tup[0])
1740 print(">port : ", tup[0][1])
1744 # this is a zcl read attribute response
1745 elif(zclCommand == '\x01'):
1747 # get the zcl payload
1748 zclPayload = parsedData['rf_data'][3:]
1749 attibuteResponseList = []
1751 # get the data for each data
1752 while(len(zclPayload) > 0):
1753 attributeId = zclPayload[0:2]
1754 attributeStatus = zclPayload[2]
1755 zclPayload = zclPayload[3:]
1757 if(ord(attributeStatus) != 0):
1758 # if attribute is not supported then it has no data
1759 # package the data and add it to the list
1760 attibuteResponseList.append((attributeId,"not_supported"))
1763 # get the data type and data length of the attributre
1764 attributeType = zclPayload[0]
1765 dataLength = zclDataTypeToBytes(zclPayload)
1767 # consume zcl payload data
1768 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1769 zclPayload = zclPayload[2:]
1770 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1771 zclPayload = zclPayload[3:]
1773 zclPayload = zclPayload[1:]
1775 # package the data and add it to the list
1776 newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength])
1777 attibuteResponseList.append(newData)
1779 # consume the data size of the payload
1780 zclPayload = zclPayload[dataLength:]
1782 # find who to send response to
1784 zigbeeSeqNumberToClientMutex.acquire()
1785 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1786 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1787 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1788 zigbeeSeqNumberToClientMutex.release()
1790 # no one to send the response to so just move on
1792 # cant really do anything here
1795 # create the response message
1797 message = "type : zcl_read_attributes_response \n"
1798 message += "packet_id: " + packetId + "\n"
1799 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1800 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1801 message += "attributes: "
1803 # create the message for each attribute
1804 for t in attibuteResponseList:
1805 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
1806 if(t[1] == "success"):
1807 attrIdStr = "%0.4x" % attrId
1808 attrIdStr = changeEndian(attrIdStr)
1810 message += attrIdStr
1812 message += "success"
1814 message += "%0.2x" % ord(t[2])
1819 dat += "%0.2x" % ord(c)
1820 dat = changeEndian(dat)
1824 attrIdStr = "%0.4x" % attrId
1825 attrIdStr = changeEndian(attrIdStr)
1827 message += attrIdStr
1829 message += "not_supported"
1832 message = message[0:len(message) - 1]
1835 sendSoceket.sendto(message,tup[0])
1841 # this is a zcl write attribute response
1842 elif(zclCommand == '\x04'):
1844 # get the zcl payload
1845 zclPayload = parsedData['rf_data'][3]
1846 # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it
1847 if(zclStatus == '\x70' or zclPayload == '\x00'):
1849 # find who to send response to
1851 zigbeeSeqNumberToClientMutex.acquire()
1852 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1853 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1854 seqNumberForNotification[longAddr] = ord(zclSeqNumber)
1855 #del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1856 zigbeeSeqNumberToClientMutex.release()
1857 # no one to send the response to so just move on
1859 # cant really do anything here
1862 # create the response message
1864 message = "type : zcl_write_attributes_response\n"
1865 message += "packet_id: " + packetId + "\n"
1866 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1867 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1868 message += "attributes: success"
1871 sendSoceket.sendto(message,tup[0])
1873 print '[ 0x0500 ] get Write Attribute Response success'
1874 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1878 print '[ 0x0500 ] get Write Attribute Response'
1879 print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data'])
1883 # this is a zcl configure attribute response
1884 elif(zclCommand == '\x07'):
1886 # find who to send response to
1888 zigbeeSeqNumberToClientMutex.acquire()
1889 if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))):
1890 tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1891 del zigbeeSeqNumberToClient[ord(zclSeqNumber)]
1892 zigbeeSeqNumberToClientMutex.release()
1894 # no one to send the response to so just move on
1896 # cant really do anything here
1900 zclPayload = parsedData['rf_data'][3:]
1902 # construct the message
1904 message = "type : zcl_configure_reporting_response \n"
1905 message += "packet_id: " + packetId + "\n"
1906 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1907 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1908 message += "attributes: "
1910 if(len(zclPayload) == 1):
1911 # if all the configurations are a success then only send back a success
1912 # based on zigbee specs
1913 message += "all_success \n";
1914 sendSoceket.sendto(message,tup[0])
1917 attibuteResponseList = []
1919 # get each attributes data
1920 while(len(zclPayload) > 0):
1921 attributeStatus = zclPayload[0]
1922 attributeDirection = zclPayload[1]
1923 attributeId = zclPayload[2:4]
1924 zclPayload = zclPayload[4:]
1926 newData = (attributeStatus,attributeDirection, attributeId)
1927 attibuteResponseList.append(newData)
1929 # package each attribute
1930 for t in attibuteResponseList:
1931 attrId = ord(t[2][0]) + (256 * ord(t[2][1]))
1932 attrIdStr = "%0.4x" % attrId
1933 attrIdStr = changeEndian(attrIdStr)
1935 message += attrIdStr
1938 message += "success"
1945 message += "reported"
1947 message += "received"
1950 message = message[0:len(message) - 1]
1952 sendSoceket.sendto(message,tup[0])
1954 # this is a zcl report attribute message
1955 elif(zclCommand == '\x0a'):
1956 print "get Report attribute "
1957 # get teh zcl payload
1958 zclPayload = parsedData['rf_data'][3:]
1959 attibuteResponseList = []
1961 # extract the attribute data
1962 while(len(zclPayload) > 0):
1963 attributeId = zclPayload[0:2]
1964 zclPayload = zclPayload[2:]
1965 attributeType = zclPayload[0]
1966 dataLength = zclDataTypeToBytes(zclPayload)
1968 if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)):
1969 zclPayload = zclPayload[2:]
1970 elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)):
1971 zclPayload = zclPayload[3:]
1973 zclPayload = zclPayload[1:]
1975 newData = (attributeId, attributeType ,zclPayload[0:dataLength])
1976 attibuteResponseList.append(newData)
1977 zclPayload = zclPayload[dataLength:]
1980 # get callback clients to respond to
1981 callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster']))
1983 zibeeHACallbackMutex.acquire()
1984 if(zibeeHACallback.has_key(callbackIndex)):
1985 retAddr = zibeeHACallback[callbackIndex]
1986 zibeeHACallbackMutex.release()
1988 # no one to respond to so do nothing here
1989 if(retAddr == None):
1992 # construct the message
1993 message = "type : zcl_report_attributes \n"
1994 message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n"
1995 message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n"
1996 message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n"
1997 message += "attributes: "
1999 # package the attributes
2000 for t in attibuteResponseList:
2001 attrId = ord(t[0][0]) + (256 * ord(t[0][1]))
2002 attrIdStr = "%0.4x" % attrId
2003 attrIdStr = changeEndian(attrIdStr)
2005 message += attrIdStr
2007 message += "%0.2x" % ord(t[1])
2012 dat += "%0.2x" % ord(c)
2013 dat = changeEndian(dat)
2017 message = message[0:len(message) - 1]
2019 print "Sending", message
2021 # send to all client that want this callback
2023 sendSoceket.sendto(message,ra)
2025 # -----------------------------------------------------------------------------
2026 # Communication Callback/Parse Methods
2027 # -----------------------------------------------------------------------------
2028 def handleNewZigbeeMessage(parsedData):
2029 ''' Method to process a zigbee message from the local radio.
2031 parsedData -- Pre-parsed (into a dict) data from message.
2033 #print "=================================================================="
2035 print "New Zigbee Message"
2036 #printMessageData(parsedData)
2038 # dispatch to the correct zigbee handler
2039 if (parsedData['id'] == 'at_response'):
2040 print "parsedDataID : at_response"
2041 processZigbeeATCommandMessage(parsedData)
2043 elif (parsedData['id'] == 'rx_explicit'):
2044 print "parsedDataID : rx_explicit"
2045 processZigbeeRxExplicitCommandMessage(parsedData)
2048 print "Unknown API format"
2050 #print "=================================================================="
2054 def handleNewUdpPacket(data, addr):
2055 ''' Method to parse and handle an incoming UDP packet.
2057 data -- Data that was in the UDP packet.
2058 addr -- Address (IP and Port) of the UDP packet origin.
2060 global ManagementPermitJoiningReqSuccess
2062 #print "=================================================================="
2064 #print "Got New UDP packet..."
2068 # data comes in as 'key: value\n key: value...' string and so needs to be
2069 # parsed into a dict
2072 # 1 key, value pair per line
2073 for line in data.split('\n'):
2075 # key and values are split based on a ':'
2076 fields = line.split(':')
2078 # make sure properly formated otherwise just ignore it
2079 if len(fields) == 2:
2081 # do strips to remove any white spacing that may have resulted
2082 # from improper packing on the sender side
2083 parsedData[fields[0].strip()] = fields[1].strip()
2086 # wrap in try statement just in case there is an improperly formated packet we
2089 # dispatch to the correct process method
2090 if(parsedData["type"] == "zdo_bind_request"):
2091 print "> processUdpZdoBindReqMessage call"
2092 processUdpZdoBindReqMessage(parsedData, addr)
2093 elif(parsedData["type"] == "zdo_unbind_request"):
2094 processUdpZdoUnBindReqMessage(parsedData, addr)
2095 elif(parsedData["type"] == "send_address"):
2096 print "> processUdpSendAddressMessage call"
2097 processUdpSendAddressMessage(parsedData, addr)
2098 elif(parsedData["type"] == "zcl_read_attributes"):
2099 processUdpZclReadAttributesMessage(parsedData, addr)
2100 elif(parsedData["type"] == "zcl_configure_reporting"):
2101 print "> zcl_configure_reporting call"
2102 processUdpZclConfigureReportingMessage(parsedData, addr)
2103 elif(parsedData["type"] == "policy_set"):
2104 processUdpPolicySet(parsedData, addr)
2105 elif(parsedData["type"] == "policy_clear"):
2106 processUdpPolicyClear(parsedData, addr)
2107 elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo
2108 processUdpManagementPermitJoiningReqMessage(parsedData, addr)
2109 elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo
2110 processUdpZclWriteAttributesMessage(parsedData, addr)
2111 elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo
2112 processUdpEnrollmentResponse(parsedData, addr)
2113 elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo
2114 processUdpBroadcastingRouteRecordReqMessage(parsedData, addr)
2115 elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo
2116 processUdpZclChangeSwitchReqMessage(parsedData, addr)
2117 elif(parsedData["type"] == "zcl_lock_or_unlock_door_request"): #made by Jiawei
2118 processUdpZclLockOrUnlockDoorReqMessage(parsedData, addr)
2119 elif(parsedData["type"] == "zcl_read_door_status_request"): #made by Jiawei
2120 processUdpZclReadDoorStatusReqMessage(parsedData, addr)
2122 #print "unknown Packet: " + parsedData["type"]
2125 # if we ever get here then something went wrong and so just ignore this
2126 # packet and try again later
2127 print "I didn't expect this error:", sys.exc_info()[0]
2128 traceback.print_exc()
2130 #print "=================================================================="
2133 # -----------------------------------------------------------------------------
2134 # Main Running Methods
2135 # -----------------------------------------------------------------------------
2138 '''Main function used for starting the application as the main driver'''
2140 global ZIGBEE_SERIAL_PORT
2141 global ZIGBEE_SERIAL_BAUD
2142 global UDP_RECEIVE_PORT
2143 global zigbeeConnection
2147 parseCommandLineArgs(sys.argv[1:])
2149 # create serial object used for communication to the zigbee radio
2150 sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD)
2152 # create a zigbee object that handles all zigbee communication
2153 # we use this to do all communication to and from the radio
2154 # when data comes from the radio it will get a bit of unpacking
2155 # and then a call to the callback specified will be done with the
2157 zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage)
2159 # get the long address of our local radio before we start doing anything
2160 getConnectedRadioLongAddress();
2162 # setup incoming UDP socket and bind it to self and specified UDP port
2163 # sending socket does not need to be bound to anything
2164 #receiveSoceket.bind(('192.168.2.227', UDP_RECEIVE_PORT))
2165 receiveSoceket.bind(('192.168.1.192', UDP_RECEIVE_PORT))
2167 # create the thread that does short address lookups
2168 addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod)
2169 addressUpdateWorkerThread.start()
2174 print "=================================================================="
2177 print "=================================================================="
2179 # wait for an incoming UDP packet
2180 # this is a blocking call
2181 data, addr = receiveSoceket.recvfrom(4096)
2183 # handle the UDP packet appropriately
2184 handleNewUdpPacket(data, addr)
2186 except KeyboardInterrupt:
2187 # use the keyboard interupt to catch a ctrl-c and kill the application
2191 # something went really wrong and so exit with error message
2192 traceback.print_exc()
2194 # signal all threads to exit
2197 # wait for threads to finish before closing of the resources
2198 addressUpdateWorkerThread.join()
2201 # make sure to close all the connections
2202 zigbeeConnection.halt()
2203 receiveSoceket.close()
2206 if __name__ == "__main__":
2207 # call main function since this is being run as the start