From: rtrimana Date: Thu, 5 Jan 2017 22:14:55 +0000 (-0800) Subject: Adding xbee_driver Python files X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1a78ccf72b7316b2973db9533a1cd7c1f04bff3a;p=iot2.git Adding xbee_driver Python files --- diff --git a/benchmarks/other/XbeePythonDriver/Coordinator_HA_Xbee_config.xml b/benchmarks/other/XbeePythonDriver/Coordinator_HA_Xbee_config.xml new file mode 100644 index 0000000..234881d --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/Coordinator_HA_Xbee_config.xml @@ -0,0 +1,77 @@ + + + + + xbp24-s2c-th_4055.xml + + 0 + 7FFF + 3 + 2 + FF + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0x20 + 1E + 0 + FF + A0000 + 3C + 0 + 3 + E8 + E8 + 11 + 0 + 4 + 1 + 0 + + + 7 + 0 + 0 + 3 + 1 + 0 + 1 + 3 + 64 + 3E8 + 2B + 20 + 1 + 0 + 1388 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 1 + 1FFF + 1FBF + 0 + 28 + 0 + 0 + 0 + + + diff --git a/benchmarks/other/XbeePythonDriver/protocol/callback_zdo_bind_request.txt b/benchmarks/other/XbeePythonDriver/protocol/callback_zdo_bind_request.txt new file mode 100644 index 0000000..dbe805c --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/protocol/callback_zdo_bind_request.txt @@ -0,0 +1,19 @@ + + +From Application to driver: +--------------------------- +type: zdo_bind_request +packet_id: +device_address_long: +cluster_id: +device_endpoint: + + + +From Driver to Application: +--------------------------- + +type: callback_register_ack +packet_id: +response: success or fail +reason: \ No newline at end of file diff --git a/benchmarks/other/XbeePythonDriver/protocol/policy.txt b/benchmarks/other/XbeePythonDriver/protocol/policy.txt new file mode 100644 index 0000000..e3de949 --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/protocol/policy.txt @@ -0,0 +1,17 @@ +--------------------------------------- + To set a new policy for the driver +---------------------------------------- + +type: policy_set +ip_address: +port: +device_address_long: + + + + + +--------------------------------------- + To clear all the policies from the driver +---------------------------------------- +type: policy_clear diff --git a/benchmarks/other/XbeePythonDriver/protocol/send_address.txt b/benchmarks/other/XbeePythonDriver/protocol/send_address.txt new file mode 100644 index 0000000..ab58ad7 --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/protocol/send_address.txt @@ -0,0 +1,14 @@ + + +From Application to driver: +--------------------------- +type: send_address +packet_id: +device_address_long: + + +From Driver to Application: +--------------------------- +type: send_address_response +packet_id: +response: success diff --git a/benchmarks/other/XbeePythonDriver/protocol/zcl_read_attributes.txt b/benchmarks/other/XbeePythonDriver/protocol/zcl_read_attributes.txt new file mode 100644 index 0000000..df41f4c --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/protocol/zcl_read_attributes.txt @@ -0,0 +1,19 @@ +From Application to driver: +--------------------------- +type: zcl_read_attributes +packet_id: +device_address_long: +device_endpoint: +cluster_id: +profile_id: +attribute_ids: [, ......] + +From Driver to Application: +--------------------------- + +type: zcl_read_attributes_response +packet_id: +device_address_long: +cluster_id: +profile_id: +attribute_ids: ; ......] diff --git a/benchmarks/other/XbeePythonDriver/xbee_digi.py b/benchmarks/other/XbeePythonDriver/xbee_digi.py new file mode 100644 index 0000000..4314bf5 --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/xbee_digi.py @@ -0,0 +1,1298 @@ +from zigbee import * +from socket import * +from select import * +import serial +import time +import collections +import sys +import getopt +import traceback +from threading import Thread, Lock +import random +import threading + + +# ----------------------------------------------------------------------------- +# Constants ans Pseudo-Constants +# ----------------------------------------------------------------------------- +UDP_RECEIVE_PORT = 5005 # port used for incoming UDP data +UDP_RECEIVE_BUFFER_SIZE = 4096 # max buffer size of an incoming UDP packet + +# time for messages to wait for a response before the system clears away that +# sequence identifier +ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5 + +# address of our local zigbee radio +ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx" + +SYSTEM_MASTER_ADDRESS = ("192.168.2.108", 12345) # ip address and portof the system master node + + +# ----------------------------------------------------------------------------- +# Global Variables and Objects +# ----------------------------------------------------------------------------- + +# zigbee communications object and its mutex +zigbeeConnection = None +zigbeeConnectionMutex = Lock() + +# zigbee mapping from long to short object dict +zigbeeLongShortAddr = dict() +zigbeeLongShortAddrMutex = Lock() + +# zigbee mapping from a sequence number to a client +# for correct response handling +zigbeeSeqNumberToClient = dict() +zigbeeSeqNumberToClientMutex = Lock() + +zigbeeBindRequest = dict() +zigbeeBindRequestMutex = Lock() + +# Keeps record of where to send callbacks to when an HA message is received +zigbeeHACallback = dict() +zigbeeHACallbackMutex = Lock() + +# Keeps a record of device addresses whose short addresses have not been +# determined yet +zigbeeUnregisteredAddresses = [] +zigbeeUnregisteredAddressesMutex = Lock() + +# used to signal all threads to end +doEndFlag = False + + +# 2 sockets, one for sending (not bound to a port manually) +# and one for receiving, known port binding by application +# both UDP sockets +sendSocket = socket(AF_INET, SOCK_DGRAM) +receiveSocket = socket(AF_INET, SOCK_DGRAM) + +# zigbee address authority list +zigbeeAddressAuthorityDict = dict() + + + +# ----------------------------------------------------------------------------- +# Helper Methods +# ----------------------------------------------------------------------------- + + +def parseCommandLineArgs(argv): + try: + opts, args = getopt.getopt( + argv, "h:u:", ["udpport="]) + + except getopt.GetoptError: + print 'test.py -u ' + sys.exit(2) + + for opt, arg in opts: + if opt == '-h': + print 'test.py -u ' + sys.exit() + + +# ------------- +# Convenience (Stateless) +# ------------- + +def shortToStr(short): + s=chr(short>>8) + chr(short & 0xff) + return s + +def hexToInt(str): + ret = 0 + for h in str: + ret = ret << 4 + ret += int(h, 16) + return ret + +def hexListToChar(hexList): + ''' Method to convert a list/string of characters into their corresponding values + + hexList -- list or string of hex characters + ''' + retString = "" + for h in hexList: + retString += chr(int(h, 16)) + return retString + +def splitByN(seq, n): + ''' Method to split a string into groups of n characters + + seq -- string + n -- group by number + ''' + return [seq[i:i+n] for i in range(0, len(seq), n)] + +def changeEndian(hexString): + ''' Method to change endian of a hex string + + hexList -- string of hex characters + ''' + split = splitByN(hexString, 2) # get each byte + split.reverse(); # reverse ordering of the bytes + + # reconstruct + retString = '' + for s in split: + retString += s + return retString + +def reverse(string): + ''' Method to change endian of a hex string + + hexList -- string of hex characters + ''' + return string[::-1] + +def printMessageData(data): + ''' Method to print a zigbee message to the console + + data -- pre-parsed zigbee message + ''' + for d in data: + print d, ' : ', + for e in data[d]: + print "{0:02x}".format(ord(e)), + if (d == 'id'): + print "({})".format(data[d]), + print + +def hexStringToZigbeeHexString(hexString): + ''' Method to change a hex string to a string of characters with the hex values + + hexList -- string of hex characters + ''' + return hexListToChar(splitByN(hexString, 2)) + +def hexStringToAddr(hexString): + ''' Method to change a hex string to a string of characters with the hex values + + hexList -- string of hex characters + ''' + split = splitByN(hexString, 2) + newstring = '[' + split[0] + for h in split[1:]: + newstring += ':' + h + newstring += ']!' + return newstring + +def addrToHexString(addr): + hex=addr[1:25] + list=[hex[i:i+2] for i in range(0, len(hex), 3)] + retstring = '' + for e in list: + retstring += e + return retstring + +def zigbeeHexStringToHexString(zigbeeHexString): + ''' Method to change string of characters with the hex values to a hex string + + hexList -- string of characters with hex values + ''' + + retString = '' + for e in zigbeeHexString: + retString += "{0:02x}".format(ord(e)) + return retString + +def zclDataTypeToBytes(zclPayload): + ''' Method to determine data length of a zcl attribute + + zclPayload -- ZCL payload data, must have dataType as first byte + ''' + attrType = ord(zclPayload[0]) + + if(attrType == 0x00): + return 0 + elif (attrType == 0x08): + return 1 + elif (attrType == 0x09): + return 2 + elif (attrType == 0x0a): + return 3 + elif (attrType == 0x0b): + return 4 + elif (attrType == 0x0c): + return 5 + elif (attrType == 0x0d): + return 6 + elif (attrType == 0x0e): + return 7 + elif (attrType == 0x0f): + return 8 + elif (attrType == 0x10): + return 1 + elif (attrType == 0x18): + return 1 + elif (attrType == 0x19): + return 2 + elif (attrType == 0x1a): + return 3 + elif (attrType == 0x1b): + return 4 + elif (attrType == 0x1c): + return 5 + elif (attrType == 0x1d): + return 6 + elif (attrType == 0x1e): + return 7 + elif (attrType == 0x1f): + return 8 + elif (attrType == 0x20): + return 1 + elif (attrType == 0x21): + return 2 + elif (attrType == 0x22): + return 3 + elif (attrType == 0x23): + return 4 + elif (attrType == 0x24): + return 5 + elif (attrType == 0x25): + return 6 + elif (attrType == 0x26): + return 7 + elif (attrType == 0x27): + return 8 + elif (attrType == 0x28): + return 1 + elif (attrType == 0x29): + return 2 + elif (attrType == 0x2a): + return 3 + elif (attrType == 0x2b): + return 4 + elif (attrType == 0x2c): + return 5 + elif (attrType == 0x2d): + return 6 + elif (attrType == 0x2e): + return 7 + elif (attrType == 0x2f): + return 8 + elif (attrType == 0x30): + return 1 + elif (attrType == 0x31): + return 2 + elif (attrType == 0x38): + return 2 + elif (attrType == 0x39): + return 4 + elif (attrType == 0x3a): + return 8 + elif (attrType == 0x41): + return ord(zclPayload[1]) + elif (attrType == 0x42): + return ord(zclPayload[1]) + elif (attrType == 0x43): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0x44): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0xe0): + return 4 + elif (attrType == 0xe1): + return 4 + elif (attrType == 0xe2): + return 4 + elif (attrType == 0xe8): + return 2 + elif (attrType == 0xe9): + return 2 + elif (attrType == 0xea): + return 4 + elif (attrType == 0xf0): + return 8 + elif (attrType == 0xf1): + return 16 + elif (attrType == 0xff): + return 0 + +# ------------- +# Other +# ------------- + +def createSequenceNumberForClient(addr, packetId): + ''' Method to get and store a sequence number with a specific client + for a specific message. + + addr -- UDP address of the client to associate with the seq. number + packetId -- packet id from the UDP packet + ''' + # keep trying to find a number to return + while(True): + + # get the current time + epoch_time = int(time.time()) + + # get the current list of used numbers + zigbeeSeqNumberToClientMutex.acquire() + keysList = zigbeeSeqNumberToClient.keys() + zigbeeSeqNumberToClientMutex.release() + + # if all the numbers are already used + if(len(keysList) == 256): + + # get a list of all the items + zigbeeSeqNumberToClientMutex.acquire() + itemsList = zigbeeSeqNumberToClient.items() + zigbeeSeqNumberToClientMutex.release() + + # search for a number that is old enough to get rid of otherwise use -1 + seqNum = -1 + for item in itemsList: + if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC): + seqNumber = item[0] + break + + if(seqNum != -1): + # replace the record with new data if we found one to replace + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + + return seqNumber + + else: + # not all numbers used yet so pick one randomly + randNum = random.randrange(0,256) + + # check if we are using the number yet + if(randNum not in keysList): + + # we are not so insert to keep track who this number is for and return it + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + return randNum + +def getConnectedRadioLongAddress(): + """ Method to make sure we get the MAC address of our local radio""" + global zigbeeConnection + global zigbeeMutex + global ZIGBEE_DEVICE_ADDRESS + + data = ddo_get_param(None, 'SH') + valuehi = "" + for e in data: + valuehi += "{0:02x}".format(ord(e)) + + data = ddo_get_param(None, 'SL') + valuelo = "" + for e in data: + valuelo += "{0:02x}".format(ord(e)) + ZIGBEE_DEVICE_ADDRESS = valuehi + valuelo + +def addressUpdateWorkerMethod(): + ''' Method to keep refreshing the short addresses of the known zigbee devices''' + global doEndFlag + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global zigbeeConnectionMutex + global zigbeeConnection + + # keep looping until signaled to quit + while(not doEndFlag): + + addrList = [] + + # add unregistered (short addresses unknown) devices so + # that we can look them up + zigbeeUnregisteredAddressesMutex.acquire() + addrList.extend(zigbeeUnregisteredAddresses) + zigbeeUnregisteredAddressesMutex.release() + + # add the devices that we have short addresses for so we can + # get their most recent short addresses + zigbeeLongShortAddrMutex.acquire() + addrList.extend(zigbeeLongShortAddr.keys()) + zigbeeLongShortAddrMutex.release() + + # Loop through all the addresses and send messages for each address + for ad in addrList: + # create payload for a query on the network for a short address + payload = '\x00' + payload += hexStringToZigbeeHexString(changeEndian(ad)) + payload += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + # The Format of the tuple is: + # (address_string, endpoint, profile_id, cluster_id) + + DESTINATION=(hexStringToAddr(ad), 0x0, 0x0, 0x0) + + zigbeeConnection.sendto(payload, 0, DESTINATION); + zigbeeConnectionMutex.release() + + time.sleep(30) + + +# ------------- +# UDP +# ------------- + +def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None): + ''' Method to send a success or fail back to a client. + + addr -- UDP address to send packet to + packetTypeStr -- name of this specific packet + packetIdStr -- packet id to send + sucOrFail -- whether this is a success or fail message (True = success) + reason -- reason of failure (if needed, default is None) + + ''' + + global sendSocket + + # construct the message + message = "type: " + packetTypeStr.strip() + "\n" + message += "packet_id: " + packetIdStr + "\n" + + if(sucOrFail): + message += "response: success \n" + else: + message += "response: fail \n" + message += "reason: " + reason + "\n" + + # send message in a UDP packet + sendSocket.sendto(message,addr) + +def processUdpZdoBindReqMessage(parsedData, addr): + ''' Method handle a zdo bind request message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeBindRequestMutex + global zigbeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + shortAddr = None + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space') + return + + # a bind request was made so must store and wait for response + # before we setup callbacks, so keep just the data we need to create the callback + zigbeeBindRequestMutex.acquire() + zigbeeBindRequest[seqNumber] = (parsedData['device_address_long'], + parsedData['cluster_id'], + parsedData['packet_id'], + addr) + zigbeeBindRequestMutex.release() + + # construct the short and long addresses of the message for sending + # make sure they are in the correct format + destLongAddr = parsedData['device_address_long'] + destShortAddr = hexStringToZigbeeHexString(shortAddr) + + # create the payload data + payloadData = "" + payloadData += chr(seqNumber) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) + payloadData += '\x03' + payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS)) + payloadData += '\x01' + + # create and send binding command + zigbeeConnectionMutex.acquire() + + # The Format of the tuple is: + # (address_string, endpoint, profile_id, cluster_id) + DESTINATION = (hexStringToAddr(destLongAddr), 0x0, 0x0, 0x21) + zigbeeConnection.sendto(payloadData, 0, DESTINATION) + + zigbeeConnectionMutex.release() + + + else: + # send a failure packet since there is no short address available + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown') + pass + + +def processUdpZdoUnBindReqMessage(parsedData, addr): + zigbeeHACallbackMutex.acquire(); + if(zigbeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])): + zigbeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr) + zigbeeHACallbackMutex.release() + sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True) + + +def processUdpSendAddressMessage(parsedData, addr): + ''' Method handle a send address command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global sendSocket + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + # construct success message + message = "type: send_address_response\n" + message += "packet_id: " + parsedData['packet_id'] + "\n" + message += "response: success\n" + + # tell client that we got their request + sendSocket.sendto(message,addr) + + # construct + zigbeeLongShortAddrMutex.acquire() + doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long']) + zigbeeLongShortAddrMutex.release() + + if(doesHaveKey): + # long address is already registered with the system so no need to do anything + return + + # long address not registered so add it for short address lookup + zigbeeUnregisteredAddressesMutex.acquire() + zigbeeUnregisteredAddresses.append(parsedData['device_address_long']) + zigbeeUnregisteredAddressesMutex.release() + +def processUdpZclReadAttributesMessage(parsedData, addr): + ''' Method handle a ZCL read attribute command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeBindRequestMutex + global zigbeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = parsedData['profile_id'] + clusterId = parsedData['cluster_id'] + dstEndpoint = parsedData['device_endpoint'] + + # get all the attributes + attributeIds = parsedData['attribute_ids'].split(',') + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x00' + + # make all the attributes payloads + for attr in attributeIds: + attr = attr.strip() + attr = changeEndian(attr) + payloadData += hexStringToZigbeeHexString(attr) + + + # (address_string, endpoint, profile_id, cluster_id) + DESTINATION = (hexStringToAddr(zigbeeHexStringToHexString(destLongAddr)), hexToInt(dstEndpoint), hexToInt(profileId), hexToInt(clusterId)) + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.sendto(payloadData, 0, DESTINATION) + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpZclConfigureReportingMessage(parsedData, addr): + ''' Method handle a zcl configure reporting message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeBindRequestMutex + global zigbeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space') + return + + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = parsedData['profile_id'] + clusterId = parsedData['cluster_id'] + dstEndpoint = parsedData['device_endpoint'] + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x06' + payloadData += '\x00' + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval'])) + + if(parsedData.has_key('reportable_change')): + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change'])) + + DESTINATION = (hexStringToAddr(zigbeeHexStringToHexString(destLongAddr)), hexToInt(dstEndpoint), hexToInt(profileId), hexToInt(clusterId)) + + # create and send binding command + zigbeeConnectionMutex.acquire() + + zigbeeConnection.sendto(payloadData, 0, DESTINATION) + zigbeeConnectionMutex.release() + + + else: + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpPolicySet(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Policy set: ", parsedData + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS: + key = (parsedData['ip_address'], int(parsedData['port'])) + if (zigbeeAddressAuthorityDict.has_key(key)): + zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long']) + else: + zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']] + +def processUdpPolicyClear(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Clear policy: ", parsedData + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS: + zigbeeAddressAuthorityDict.clear() + + +def processZigbeeRxExplicitCommandMessage(rawData, src_addr): + ''' Method to process a rx-explicit zigbee message + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + +# The format for addr is the tuple (address_string, endpoint, profile_id, cluster_id). + global zigbeeBindRequestMutex + global zigbeeBindRequest + parsedData = dict() + parsedData['source_addr_long']=hexStringToZigbeeHexString(addrToHexString(src_addr[0])) + parsedData['cluster']=shortToStr(src_addr[3]) + parsedData['profile']=shortToStr(src_addr[2]) + parsedData['rf_data']=rawData + + # get the long and short addresses from the message payload since we can + # use these to update the short addresses since this short address is fresh + longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long']) + + # check if this short address is for a device that has yet to be + # registered + + # if this is a ZDO message/response + if(parsedData['profile'] == '\x00\x00'): + + # if this is a device announcement so we can get some useful data from it + if(parsedData['cluster'] == '\x00\x13'): + + # pick out the correct parts of the payload + longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11]) + shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3]) + + # change the endian of the address + longAddr = changeEndian(longAddr) + shortAddr = changeEndian(shortAddr) + + # update the table with the new information + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + # check if this short address is for a device that has yet to be + # registered + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # if this is a response to a zdo bind_req message + elif(parsedData['cluster'] == '\x80\x21'): + + # get the status and sequence number from the message + seqNumber = parsedData['rf_data'][0] + statusCode = parsedData['rf_data'][1] + + # get the bind tuple information + # for this specific bind request + tup = None + zigbeeBindRequestMutex.acquire() + if(zigbeeBindRequest.has_key(ord(seqNumber))): + tup = zigbeeBindRequest[ord(seqNumber)] + zigbeeBindRequestMutex.release() + + if(tup == None): + # cant really do anything in this case... + # don't have any information on who the data is for + return + + # successful binding + if(ord(statusCode) == 0): + + # add a callback for this specific device and cluster + # to the HA callback dict + if(zigbeeHACallback.has_key((tup[0], tup[1]))): + if(tup[3] not in zigbeeHACallback[(tup[0], tup[1])]): + zigbeeHACallback[(tup[0], tup[1])].append(tup[3]) + else: + zigbeeHACallback[(tup[0], tup[1])] = [tup[3]] + + # send success message + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True) + + # Not Supported + elif (ord(statusCode) == 170): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported') + + # Table Full + elif (ord(statusCode) == 174): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full') + + # Other issue, dont have code for + else: + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other') + + # if this is a response to a short address query + elif(parsedData['cluster'] == '\x80\x00'): + + # get a status code + statusCode = parsedData['rf_data'][0] + + # does not matter if this is not a success, we can try again later + if(statusCode != '\x00'): + # status code was not success so do not do anything + return + + # get the short and long address information + longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10])) + shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12])) + + # remove device from list of unregistered devices if it is in it + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # update/insert the short address + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + # if this is a home automation zcl message/response + elif (parsedData['profile'] == '\x01\x04'): + + # get the zcl message header + zclFrameControl = parsedData['rf_data'][0] + zclSeqNumber = parsedData['rf_data'][1] + zclCommand = parsedData['rf_data'][2] + + # this is a zcl read attribute response + if(zclCommand == '\x01'): + + # get the zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # get the data for each data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + attributeStatus = zclPayload[2] + zclPayload = zclPayload[3:] + + if(ord(attributeStatus) != 0): + # if attribute is not supported then it has no data + # package the data and add it to the list + attibuteResponseList.append((attributeId,"not_supported")) + else: + + # get the data type and data length of the attributre + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + # consume zcl payload data + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + # package the data and add it to the list + newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + + # consume the data size of the payload + zclPayload = zclPayload[dataLength:] + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # create the response message + packetId = tup[2] + message = "type : zcl_read_attributes_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # create the message for each attribute + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + if(t[1] == "success"): + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "success" + message += ", " + message += "%0.2x" % ord(t[2]) + message += ", " + + dat = "" + for c in (t[3]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + else: + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "not_supported" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + + # send the socket + sendSocket.sendto(message,tup[0]) + + # this is a zcl configure attribute response + elif(zclCommand == '\x07'): + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # get zcl payload + zclPayload = parsedData['rf_data'][3:] + + # construct the message + packetId = tup[2] + message = "type : zcl_configure_reporting_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + if(len(zclPayload) == 1): + # if all the configurations are a success then only send back a success + # based on zigbee specs + message += "all_success \n"; + sendSocket.sendto(message,tup[0]) + + else: + attibuteResponseList = [] + + # get each attributes data + while(len(zclPayload) > 0): + attributeStatus = zclPayload[0] + attributeDirection = zclPayload[1] + attributeId = zclPayload[2:4] + zclPayload = zclPayload[4:] + + newData = (attributeStatus,attributeDirection, attributeId) + attibuteResponseList.append(newData) + + # package each attribute + for t in attibuteResponseList: + attrId = ord(t[2][0]) + (256 * ord(t[2][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + if(ord(t[0]) == 0): + message += "success" + else: + message += "error" + + message += ", " + + if(ord(t[1]) == 0): + message += "reported" + else: + message += "received" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + sendSocket.sendto(message,tup[0]) + + # this is a zcl report attribute message + elif(zclCommand == '\x0a'): + + # get teh zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # extract the attribute data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + zclPayload = zclPayload[2:] + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + newData = (attributeId, attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + zclPayload = zclPayload[dataLength:] + + + # get callback clients to respond to + callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster'])) + retAddr = None + zigbeeHACallbackMutex.acquire() + if(zigbeeHACallback.has_key(callbackIndex)): + retAddr = zigbeeHACallback[callbackIndex] + zigbeeHACallbackMutex.release() + + # no one to respond to so do nothing here + if(retAddr == None): + return + + # construct the message + message = "type : zcl_report_attributes \n" + message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # package the attributes + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "%0.2x" % ord(t[1]) + message += ", " + + dat = "" + for c in (t[2]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + + # send to all client that want this callback + for ra in retAddr: + sendSocket.sendto(message,ra) + + +# ----------------------------------------------------------------------------- +# Communication Callback/Parse Methods +# ----------------------------------------------------------------------------- +def handleNewZigbeeMessage(parsedData, src_addr): + ''' Method to process a zigbee message from the local radio. + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + processZigbeeRxExplicitCommandMessage(parsedData, src_addr) + + +def handleNewUdpPacket(data, addr): + ''' Method to parse and handle an incoming UDP packet. + + data -- Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + + + # data comes in as 'key: value\n key: value...' string and so needs to be + # parsed into a dict + parsedData = dict() + + # 1 key, value pair per line + for line in data.split('\n'): + + # key and values are split based on a ':' + fields = line.split(':') + + # make sure properly formated otherwise just ignore it + if len(fields) == 2: + + # do strips to remove any white spacing that may have resulted + # from improper packing on the sender side + parsedData[fields[0].strip()] = fields[1].strip() + + # wrap in try statement just in case there is an improperly formated packet we + # can deal with it + try: + # dispatch to the correct process method + if(parsedData["type"] == "zdo_bind_request"): + processUdpZdoBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "zdo_unbind_request"): + processUdpZdoUnBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "send_address"): + processUdpSendAddressMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_read_attributes"): + processUdpZclReadAttributesMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_configure_reporting"): + processUdpZclConfigureReportingMessage(parsedData, addr) + elif(parsedData["type"] == "policy_set"): + processUdpPolicySet(parsedData, addr) + elif(parsedData["type"] == "policy_clear"): + processUdpPolicyClear(parsedData, addr) + else: + pass + except: + # if we ever get here then something went wrong and so just ignore this + # packet and try again later + print "I didn't expect this error:", sys.exc_info()[0] + traceback.print_exc() + + + +def pollMessages(): + payload, src_addr = zigbeeConnection.recvfrom(1024) + handleNewZigbeeMessage(payload, src_addr) + + +# ----------------------------------------------------------------------------- +# Main Running Methods +# ----------------------------------------------------------------------------- + +def main(): + '''Main function used for starting the application as the main driver''' + + global UDP_RECEIVE_PORT + global zigbeeConnection + global zigbeeMutex + global doEndFlag + + parseCommandLineArgs(sys.argv[1:]) + + + # create a zigbee object that handles all zigbee communication + # we use this to do all communication to and from the radio + # when data comes from the radio it will get a bit of unpacking + # and then a call to the callback specified will be done with the + # unpacked data + zigbeeConnection = socket(AF_ZIGBEE, SOCK_DGRAM, XBS_PROT_APS) + + zigbeeConnection.bind(("", 0x0, 0, 0)) + + # get the long address of our local radio before we start doing anything + getConnectedRadioLongAddress(); + + # setup incoming UDP socket and bind it to self and specified UDP port + # sending socket does not need to be bound to anything + receiveSocket.bind(("", UDP_RECEIVE_PORT)) + + # create the thread that does short address lookups + addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod) + addressUpdateWorkerThread.start() + zigbeeConnection.setblocking(0) + receiveSocket.setblocking(0) + + + try: + # Main running loop + while(True): + rlist = [ receiveSocket, zigbeeConnection ] + wlist = [] + xlist = [] + rlist, wlist, xlist = select(rlist, [], []) + if zigbeeConnection in rlist: + pollMessages() + + if receiveSocket in rlist: + data, addr = receiveSocket.recvfrom(4096) + handleNewUdpPacket(data, addr) + + except KeyboardInterrupt: + # use the keyboard interupt to catch a ctrl-c and kill the application + pass + + except: + # something went really wrong and so exit with error message + traceback.print_exc() + + # signal all threads to exit + doEndFlag = True + + # wait for threads to finish before closing of the resources + addressUpdateWorkerThread.join() + + + # make sure to close all the connections + zigbeeConnection.close() + receiveSocket.close() + sendSocket.close() + +if __name__ == "__main__": + # call main function since this is being run as the start + main() diff --git a/benchmarks/other/XbeePythonDriver/xbee_driver.py b/benchmarks/other/XbeePythonDriver/xbee_driver.py new file mode 100644 index 0000000..84e7a0b --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/xbee_driver.py @@ -0,0 +1,1342 @@ +from xbee import ZigBee +import serial +import time +import collections +import sys +import getopt +from socket import * +import traceback +from threading import Thread, Lock +import random +import threading + + +# ----------------------------------------------------------------------------- +# Constants ans Pseudo-Constants +# ----------------------------------------------------------------------------- +UDP_RECEIVE_PORT = 5005 # port used for incoming UDP data +UDP_RECEIVE_BUFFER_SIZE = 4096 # max buffer size of an incoming UDP packet +SYSTEM_MASTER_ADDRESS = ("192.168.2.108", 12345) # ip address and portof the system master node computer ip addr running java + +# time for messages to wait for a response before the system clears away that +# sequence identifier +ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5 + +ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DJIP" # USB-Serial port of local radio +ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port + +# address of our local zigbee radio +ZIGBEE_DEVICE_ADDRESS = "xxxxxxxxxxxxxxxx" + +# ----------------------------------------------------------------------------- +# Global Variables and Objects +# ----------------------------------------------------------------------------- + +# signals to see if a request needs to be made +didGetLocalRadioHighAddress = False; +didGetLocalRadioLowAddress = False; + +# zigbee communications object and its mutex +zigbeeConnection = None +zigbeeConnectionMutex = Lock() + +# zigbee mapping from long to short object dict +zigbeeLongShortAddr = dict() +zigbeeLongShortAddrMutex = Lock() + +# zigbee mapping from a sequence number to a client +# for correct response handling +zigbeeSeqNumberToClient = dict() +zigbeeSeqNumberToClientMutex = Lock() + +zigeeBindRequest = dict() +zigeeBindRequestMutex = Lock() + +# Keeps record of where to send callbacks to when an HA message is received +zibeeHACallback = dict() +zibeeHACallbackMutex = Lock() + + +# Keeps a record of device addresses whose short addresses have not been +# determined yet +zigbeeUnregisteredAddresses = [] +zigbeeUnregisteredAddressesMutex = Lock() + +# used to signal all threads to end +doEndFlag = False + + +# 2 sockets, one for sending (not bound to a port manually) +# and one for receiving, known port binding by application +# both UDP sockets +sendSoceket = socket(AF_INET, SOCK_DGRAM) +receiveSoceket = socket(AF_INET, SOCK_DGRAM) + + +# zigbee address authority list +zigbeeAddressAuthorityDict = dict() + +# ----------------------------------------------------------------------------- +# Helper Methods +# ----------------------------------------------------------------------------- + + +def parseCommandLineArgs(argv): + global ZIGBEE_SERIAL_PORT + global ZIGBEE_SERIAL_BAUD + try: + opts, args = getopt.getopt( + argv, "hp:b:u:", ["port=", "baud=", "udpport="]) + + except getopt.GetoptError: + print 'test.py -p -b -u ' + sys.exit(2) + + for opt, arg in opts: + if opt == '-h': + print 'test.py -p -b -u ' + sys.exit() + + elif opt in ("-p", "--port"): + ZIGBEE_SERIAL_PORT = arg + + elif opt in ("-b", "--baud"): + try: + ZIGBEE_SERIAL_BAUD = int(arg) + except ValueError: + print "Buad rate must be an integer" + sys.exit() + + +# ------------- +# Convenience (Stateless) +# ------------- + +def hexListToChar(hexList): + ''' Method to convert a list/string of characters into their corresponding values + + hexList -- list or string of hex characters + ''' + retString = "" + for h in hexList: + retString += chr(int(h, 16)) + return retString + +def splitByN(seq, n): + ''' Method to split a string into groups of n characters + + seq -- string + n -- group by number + ''' + return [seq[i:i+n] for i in range(0, len(seq), n)] + +def changeEndian(hexString): + ''' Method to change endian of a hex string + + hexList -- string of hex characters + ''' + split = splitByN(hexString, 2) # get each byte + split.reverse(); # reverse ordering of the bytes + + # reconstruct + retString = '' + for s in split: + retString += s + return retString + +def printMessageData(data): + ''' Method to print a zigbee message to the console + + data -- pre-parsed zigbee message + ''' + for d in data: + print d, ' : ', + for e in data[d]: + print "{0:02x}".format(ord(e)), + if (d == 'id'): + print "({})".format(data[d]), + print + +def hexStringToZigbeeHexString(hexString): + ''' Method to change a hex string to a string of characters with the hex values + + hexList -- string of hex characters + ''' + return hexListToChar(splitByN(hexString, 2)) + +def zigbeeHexStringToHexString(zigbeeHexString): + ''' Method to change string of characters with the hex values to a hex string + + hexList -- string of characters with hex values + ''' + + retString = '' + for e in zigbeeHexString: + retString += "{0:02x}".format(ord(e)) + return retString + +def zclDataTypeToBytes(zclPayload): + ''' Method to determine data length of a zcl attribute + + zclPayload -- ZCL payload data, must have dataType as first byte + ''' + attrType = ord(zclPayload[0]) + + if(attrType == 0x00): + return 0 + elif (attrType == 0x08): + return 1 + elif (attrType == 0x09): + return 2 + elif (attrType == 0x0a): + return 3 + elif (attrType == 0x0b): + return 4 + elif (attrType == 0x0c): + return 5 + elif (attrType == 0x0d): + return 6 + elif (attrType == 0x0e): + return 7 + elif (attrType == 0x0f): + return 8 + elif (attrType == 0x10): + return 1 + elif (attrType == 0x18): + return 1 + elif (attrType == 0x19): + return 2 + elif (attrType == 0x1a): + return 3 + elif (attrType == 0x1b): + return 4 + elif (attrType == 0x1c): + return 5 + elif (attrType == 0x1d): + return 6 + elif (attrType == 0x1e): + return 7 + elif (attrType == 0x1f): + return 8 + elif (attrType == 0x20): + return 1 + elif (attrType == 0x21): + return 2 + elif (attrType == 0x22): + return 3 + elif (attrType == 0x23): + return 4 + elif (attrType == 0x24): + return 5 + elif (attrType == 0x25): + return 6 + elif (attrType == 0x26): + return 7 + elif (attrType == 0x27): + return 8 + elif (attrType == 0x28): + return 1 + elif (attrType == 0x29): + return 2 + elif (attrType == 0x2a): + return 3 + elif (attrType == 0x2b): + return 4 + elif (attrType == 0x2c): + return 5 + elif (attrType == 0x2d): + return 6 + elif (attrType == 0x2e): + return 7 + elif (attrType == 0x2f): + return 8 + elif (attrType == 0x30): + return 1 + elif (attrType == 0x31): + return 2 + elif (attrType == 0x38): + return 2 + elif (attrType == 0x39): + return 4 + elif (attrType == 0x3a): + return 8 + elif (attrType == 0x41): + return ord(zclPayload[1]) + elif (attrType == 0x42): + return ord(zclPayload[1]) + elif (attrType == 0x43): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0x44): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0xe0): + return 4 + elif (attrType == 0xe1): + return 4 + elif (attrType == 0xe2): + return 4 + elif (attrType == 0xe8): + return 2 + elif (attrType == 0xe9): + return 2 + elif (attrType == 0xea): + return 4 + elif (attrType == 0xf0): + return 8 + elif (attrType == 0xf1): + return 16 + elif (attrType == 0xff): + return 0 + +# ------------- +# Other +# ------------- + +def createSequenceNumberForClient(addr, packetId): + ''' Method to get and store a sequence number with a specific client + for a specific message. + + addr -- UDP address of the client to associate with the seq. number + packetId -- packet id from the UDP packet + ''' + # keep trying to find a number to return + while(True): + + # get the current time + epoch_time = int(time.time()) + + # get the current list of used numbers + zigbeeSeqNumberToClientMutex.acquire() + keysList = zigbeeSeqNumberToClient.keys() + zigbeeSeqNumberToClientMutex.release() + + # if all the numbers are already used + if(len(keysList) == 256): + + # get a list of all the items + zigbeeSeqNumberToClientMutex.acquire() + itemsList = zigbeeSeqNumberToClient.items() + zigbeeSeqNumberToClientMutex.release() + + # search for a number that is old enough to get rid of otherwise use -1 + seqNum = -1 + for item in itemsList: + if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC): + seqNumber = item[0] + break + + if(seqNum != -1): + # replace the record with new data if we found one to replace + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + + return seqNumber + + else: + # not all numbers used yet so pick one randomly + randNum = random.randrange(0,256) + + # check if we are using the number yet + if(randNum not in keysList): + + # we are not so insert to keep track who this number is for and return it + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + return randNum + +def getConnectedRadioLongAddress(): + """ Method to make sure we get the MAC address of our local radio""" + global zigbeeConnection + global zigbeeMutex + + # keep looping until we get both the MSBs and the LSBs + while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)): + + # reissue requests + zigbeeConnection.send('at', command="SH") + zigbeeConnection.send('at', command="SL") + + # sleep for a bit to give the radio time to respond before we check again + time.sleep(0.5) + +def addressUpdateWorkerMethod(): + ''' Method to keep refreshing the short addresses of the known zigbee devices''' + global doEndFlag + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global zigbeeConnectionMutex + global zigbeeConnection + + # keep looping until signaled to quit + while(not doEndFlag): + + addrList = [] + + # add unregistered (short addresses unknown) devices so + # that we can look them up + zigbeeUnregisteredAddressesMutex.acquire() + addrList.extend(zigbeeUnregisteredAddresses) + zigbeeUnregisteredAddressesMutex.release() + + # add the devices that we have short addresses for so we can + # get their most recent short addresses + zigbeeLongShortAddrMutex.acquire() + addrList.extend(zigbeeLongShortAddr.keys()) + zigbeeLongShortAddrMutex.release() + + # Loop through all the addresses and send messages for each address + for ad in addrList: + + # create payload for a query on the network for a short address + payload = '\x00' + payload += hexStringToZigbeeHexString(changeEndian(ad)) + payload += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + dest_addr_long=hexStringToZigbeeHexString(ad), + dest_addr='\xff\xfd', + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x00', + profile='\x00\x00', + data=payload + ) + zigbeeConnectionMutex.release() + + time.sleep(1) + + +# ------------- +# UDP +# ------------- + +def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None): + ''' Method to send a success or fail back to a client. + + addr -- UDP address to send packet to + packetTypeStr -- name of this specific packet + packetIdStr -- packet id to send + sucOrFail -- whether this is a success or fail message (True = success) + reason -- reason of failure (if needed, default is None) + + ''' + + global sendSoceket + + # construct the message + message = "type: " + packetTypeStr.strip() + "\n" + message += "packet_id: " + packetIdStr + "\n" + + if(sucOrFail): + message += "response: success \n" + else: + message += "response: fail \n" + message += "reason: " + reason + "\n" + + # send message in a UDP packet + sendSoceket.sendto(message,addr) + +def processUdpZdoBindReqMessage(parsedData, addr): + + shortAddr = None + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space') + return + + # a bind request was made so must store and wait for response + # before we setup callbacks, so keep just the data we need to create the callback + zigeeBindRequestMutex.acquire() + zigeeBindRequest[seqNumber] = (parsedData['device_address_long'], + parsedData['cluster_id'], + parsedData['packet_id'], + addr) + zigeeBindRequestMutex.release() + + # construct the short and long addresses of the message for sending + # make sure they are in the correct format + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + + # create the payload data + payloadData = "" + payloadData += chr(seqNumber) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) + payloadData += '\x03' + payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS)) + payloadData += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x21', + profile='\x00\x00', + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + # send a failure packet since there is no short address available + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpZdoUnBindReqMessage(parsedData, addr): + zibeeHACallbackMutex.acquire(); + if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])): + zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr) + zibeeHACallbackMutex.release() + sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True) + + + +def processUdpSendAddressMessage(parsedData, addr): + ''' Method handle a send address command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global sendSoceket + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + # construct success message + message = "type: send_address_response\n" + message += "packet_id: " + parsedData['packet_id'] + "\n" + message += "response: success\n" + + # tell client that we got their request + sendSoceket.sendto(message,addr) + + # construct + zigbeeLongShortAddrMutex.acquire() + doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long']) + zigbeeLongShortAddrMutex.release() + + if(doesHaveKey): + # long address is already registered with the system so no need to do anything + return + + # long address not registered so add it for short address lookup + zigbeeUnregisteredAddressesMutex.acquire() + zigbeeUnregisteredAddresses.append(parsedData['device_address_long']) + zigbeeUnregisteredAddressesMutex.release() + +def processUdpZclReadAttributesMessage(parsedData, addr): + ''' Method handle a ZCL read attribute command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # get all the attributes + attributeIds = parsedData['attribute_ids'].split(',') + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x00' + + # make all the attributes payloads + for attr in attributeIds: + attr = attr.strip() + attr = changeEndian(attr) + payloadData += hexStringToZigbeeHexString(attr) + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpZclConfigureReportingMessage(parsedData, addr): + ''' Method handle a zcl configure reporting message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space') + return + + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x06' + payloadData += '\x00' + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval'])) + + if(parsedData.has_key('reportable_change')): + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change'])) + + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpPolicySet(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Policy set: ", parsedData + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS: + key = (parsedData['ip_address'], int(parsedData['port'])) + if (zigbeeAddressAuthorityDict.has_key(key)): + zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long']) + else: + zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']] + +def processUdpPolicyClear(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Clear policy: ", parsedData + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS: + zigbeeAddressAuthorityDict.clear() + +# ------------- +# Zigbee +# ------------- + +def processZigbeeATCommandMessage(parsedData): + ''' Method to process an AT zigbee message + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + global ZIGBEE_DEVICE_ADDRESS + global didGetLocalRadioHighAddress + global didGetLocalRadioLowAddress + + # command response for the high bytes of the local device long address + if(parsedData['command'] == 'SH'): + # convert the parameter to a string value (human readable) + value = "" + for e in parsedData['parameter']: + value += "{0:02x}".format(ord(e)) + + # set the correct portion of the address + ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:] + + #signal that we got this part of the address + didGetLocalRadioHighAddress = True + + # command response for the low bytes of the local device long address + elif(parsedData['command'] == 'SL'): + # convert the parameter to a string value (human readable) + value = "" + for e in parsedData['parameter']: + value += "{0:02x}".format(ord(e)) + + # set the correct portion of the address + ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value + + #signal that we got this part of the address + didGetLocalRadioLowAddress = True + +def processZigbeeRxExplicitCommandMessage(parsedData): + ''' Method to process a rx-explicit zigbee message + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + global zigeeBindRequestMutex + global zigeeBindRequest + + # get the long and short addresses from the message payload since we can + # use these to update the short addresses since this short address is fresh + longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long']) + shortAddr = zigbeeHexStringToHexString( parsedData['source_addr']) + + # check if this short address is for a device that has yet to be + # registered + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # update/ or insert the short address + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + + # if this is a ZDO message/response + if(parsedData['profile'] == '\x00\x00'): + + # if this is a device announcement so we can get some useful data from it + if(parsedData['cluster'] == '\x00\x13'): + + # pick out the correct parts of the payload + longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11]) + shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3]) + + # change the endian of the address + longAddr = changeEndian(longAddr) + shortAddr = changeEndian(shortAddr) + + # update the table with the new information + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + # check if this short address is for a device that has yet to be + # registered + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # if this is a response to a zdo bind_req message + elif(parsedData['cluster'] == '\x80\x21'): + + # get the status and sequence number from the message + seqNumber = parsedData['rf_data'][0] + statusCode = parsedData['rf_data'][1] + + # get the bind tuple information + # for this specific bind request + tup = None + zigeeBindRequestMutex.acquire() + if(zigeeBindRequest.has_key(ord(seqNumber))): + tup = zigeeBindRequest[ord(seqNumber)] + zigeeBindRequestMutex.release() + + if(tup == None): + # cant really do anything in this case... + # don't have any information on who the data is for + return + + # successful binding + if(ord(statusCode) == 0): + + # add a callback for this specific device and cluster + # to the HA callback dict + zibeeHACallbackMutex.acquire(); + if(zibeeHACallback.has_key((tup[0], tup[1]))): + if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]): + zibeeHACallback[(tup[0], tup[1])].append(tup[3]) + else: + zibeeHACallback[(tup[0], tup[1])] = [tup[3]] + zibeeHACallbackMutex.release() + + # send success message + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True) + + # Not Supported + elif (ord(statusCode) == 170): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported') + + # Table Full + elif (ord(statusCode) == 174): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full') + + # Other issue, dont have code for + else: + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other') + + # if this is a response to a short address query + elif(parsedData['cluster'] == '\x80\x00'): + + # get a status code + statusCode = parsedData['rf_data'][0] + + # does not matter if this is not a success, we can try again later + if(statusCode != '\x00'): + # status code was not success so do not do anything + return + + # get the short and long address information + longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10])) + shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12])) + + # remove device from list of unregistered devices if it is in it + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # update/insert the short address + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + # if this is a home automation zcl message/response + elif (parsedData['profile'] == '\x01\x04'): + + # get the zcl message header + zclFrameControl = parsedData['rf_data'][0] + zclSeqNumber = parsedData['rf_data'][1] + zclCommand = parsedData['rf_data'][2] + + # this is a zcl read attribute response + if(zclCommand == '\x01'): + + # get the zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # get the data for each data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + attributeStatus = zclPayload[2] + zclPayload = zclPayload[3:] + + if(ord(attributeStatus) != 0): + # if attribute is not supported then it has no data + # package the data and add it to the list + attibuteResponseList.append((attributeId,"not_supported")) + else: + + # get the data type and data length of the attributre + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + # consume zcl payload data + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + # package the data and add it to the list + newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + + # consume the data size of the payload + zclPayload = zclPayload[dataLength:] + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # create the response message + packetId = tup[2] + message = "type : zcl_read_attributes_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # create the message for each attribute + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + if(t[1] == "success"): + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "success" + message += ", " + message += "%0.2x" % ord(t[2]) + message += ", " + + dat = "" + for c in (t[3]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + else: + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "not_supported" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + + # send the socket + sendSoceket.sendto(message,tup[0]) + + # this is a zcl configure attribute response + elif(zclCommand == '\x07'): + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # get zcl payload + zclPayload = parsedData['rf_data'][3:] + + # construct the message + packetId = tup[2] + message = "type : zcl_configure_reporting_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + if(len(zclPayload) == 1): + # if all the configurations are a success then only send back a success + # based on zigbee specs + message += "all_success \n"; + sendSoceket.sendto(message,tup[0]) + + else: + attibuteResponseList = [] + + # get each attributes data + while(len(zclPayload) > 0): + attributeStatus = zclPayload[0] + attributeDirection = zclPayload[1] + attributeId = zclPayload[2:4] + zclPayload = zclPayload[4:] + + newData = (attributeStatus,attributeDirection, attributeId) + attibuteResponseList.append(newData) + + # package each attribute + for t in attibuteResponseList: + attrId = ord(t[2][0]) + (256 * ord(t[2][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + if(ord(t[0]) == 0): + message += "success" + else: + message += "error" + + message += ", " + + if(ord(t[1]) == 0): + message += "reported" + else: + message += "received" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + sendSoceket.sendto(message,tup[0]) + + # this is a zcl report attribute message + elif(zclCommand == '\x0a'): + + # get teh zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # extract the attribute data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + zclPayload = zclPayload[2:] + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + newData = (attributeId, attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + zclPayload = zclPayload[dataLength:] + + + # get callback clients to respond to + callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster'])) + retAddr = None + zibeeHACallbackMutex.acquire() + if(zibeeHACallback.has_key(callbackIndex)): + retAddr = zibeeHACallback[callbackIndex] + zibeeHACallbackMutex.release() + + # no one to respond to so do nothing here + if(retAddr == None): + return + + # construct the message + message = "type : zcl_report_attributes \n" + message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # package the attributes + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "%0.2x" % ord(t[1]) + message += ", " + + dat = "" + for c in (t[2]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + + # send to all client that want this callback + for ra in retAddr: + sendSoceket.sendto(message,ra) + +# ----------------------------------------------------------------------------- +# Communication Callback/Parse Methods +# ----------------------------------------------------------------------------- +def handleNewZigbeeMessage(parsedData): + ''' Method to process a zigbee message from the local radio. + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + print "==================================================================" + print "New Zigbee Message" + # printMessageData(parsedData) + + # dispatch to the correct zigbee handler + if (parsedData['id'] == 'at_response'): + processZigbeeATCommandMessage(parsedData) + + elif (parsedData['id'] == 'rx_explicit'): + #printMessageData(parsedData) + processZigbeeRxExplicitCommandMessage(parsedData) + + else: + print "Unknown API format" + + print "==================================================================" + +def handleNewUdpPacket(data, addr): + ''' Method to parse and handle an incoming UDP packet. + + data -- Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + print "==================================================================" + print "Got New UDP packet..." + # print data + + + # data comes in as 'key: value\n key: value...' string and so needs to be + # parsed into a dict + parsedData = dict() + + # 1 key, value pair per line + for line in data.split('\n'): + + # key and values are split based on a ':' + fields = line.split(':') + + # make sure properly formated otherwise just ignore it + if len(fields) == 2: + + # do strips to remove any white spacing that may have resulted + # from improper packing on the sender side + parsedData[fields[0].strip()] = fields[1].strip() + + # wrap in try statement just in case there is an improperly formated packet we + # can deal with it + try: + # dispatch to the correct process method + if(parsedData["type"] == "zdo_bind_request"): + processUdpZdoBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "zdo_unbind_request"): + processUdpZdoUnBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "send_address"): + processUdpSendAddressMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_read_attributes"): + processUdpZclReadAttributesMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_configure_reporting"): + processUdpZclConfigureReportingMessage(parsedData, addr) + elif(parsedData["type"] == "policy_set"): + processUdpPolicySet(parsedData, addr) + elif(parsedData["type"] == "policy_clear"): + processUdpPolicyClear(parsedData, addr) + else: + #print "unknown Packet: " + parsedData["type"] + pass + except: + # if we ever get here then something went wrong and so just ignore this + # packet and try again later + print "I didn't expect this error:", sys.exc_info()[0] + traceback.print_exc() + + print "==================================================================" + + +# ----------------------------------------------------------------------------- +# Main Running Methods +# ----------------------------------------------------------------------------- + +def main(): + '''Main function used for starting the application as the main driver''' + + global ZIGBEE_SERIAL_PORT + global ZIGBEE_SERIAL_BAUD + global UDP_RECEIVE_PORT + global zigbeeConnection + global zigbeeMutex + global doEndFlag + + parseCommandLineArgs(sys.argv[1:]) + + # create serial object used for communication to the zigbee radio + sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD) + + # create a zigbee object that handles all zigbee communication + # we use this to do all communication to and from the radio + # when data comes from the radio it will get a bit of unpacking + # and then a call to the callback specified will be done with the + # unpacked data + zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage) + + # get the long address of our local radio before we start doing anything + getConnectedRadioLongAddress(); + + # setup incoming UDP socket and bind it to self and specified UDP port + # sending socket does not need to be bound to anything + receiveSoceket.bind(('127.0.0.1', UDP_RECEIVE_PORT)) + + # create the thread that does short address lookups + addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod) + addressUpdateWorkerThread.start() + + try: + # Main running loop + while(True): + print "==================================================================" + print "Waiting..." + print "==================================================================" + + # wait for an incoming UDP packet + # this is a blocking call + data, addr = receiveSoceket.recvfrom(4096) + + # handle the UDP packet appropriately + handleNewUdpPacket(data, addr) + + except KeyboardInterrupt: + # use the keyboard interupt to catch a ctrl-c and kill the application + pass + + except: + # something went really wrong and so exit with error message + traceback.print_exc() + + # signal all threads to exit + doEndFlag = True + + # wait for threads to finish before closing of the resources + addressUpdateWorkerThread.join() + + + # make sure to close all the connections + zigbeeConnection.halt() + receiveSoceket.close() + sendSoceket.close() + +if __name__ == "__main__": + # call main function since this is being run as the start + main() diff --git a/benchmarks/other/XbeePythonDriver/xbee_driver_smartthings.py b/benchmarks/other/XbeePythonDriver/xbee_driver_smartthings.py new file mode 100644 index 0000000..ea13855 --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/xbee_driver_smartthings.py @@ -0,0 +1,1889 @@ +from xbee import ZigBee +import serial +import time +import collections +import sys +import getopt +from socket import * +import traceback +from threading import Thread, Lock +import random +import threading + + +# ----------------------------------------------------------------------------- +# Constants ans Pseudo-Constants +# ----------------------------------------------------------------------------- +UDP_RECEIVE_PORT = 5005 # port used for incoming UDP data +UDP_RECEIVE_BUFFER_SIZE = 4096 # max buffer size of an incoming UDP packet +SYSTEM_MASTER_ADDRESS = ("127.0.0.1", 12345) # ip address and portof the system master node +#SYSTEM_MASTER_ADDRESS = ("127.0.0.1", 22222) # ip address and portof the system master node +#SYSTEM_MASTER_ADDRESS2 = ("127.0.0.1", 11111) +#SYSTEM_MASTER_ADDRESS3 = ("127.0.0.1", 11222) + +# time for messages to wait for a response before the system clears away that +# sequence identifier +ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC = 5 + +#ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DCRH" # USB-Serial port of local radio +ZIGBEE_SERIAL_PORT = "/dev/ttyUSB0" +ZIGBEE_SERIAL_BAUD = 115200 # Baud rate for above port + +# address of our local zigbee radio +ZIGBEE_DEVICE_ADDRESS = "0013a20040d99cb4" + +# ----------------------------------------------------------------------------- +# Global Variables and Objects +# ----------------------------------------------------------------------------- + +# signals to see if a request needs to be made +didGetLocalRadioHighAddress = False; +didGetLocalRadioLowAddress = False; + +# zigbee communications object and its mutex +zigbeeConnection = None +zigbeeConnectionMutex = Lock() + +#singleton mabe by changwoo +matchDescriptorReqSingleton = True +deviceAnnouncementSingleton = True +ManagementPermitJoiningReqSuccess = False + +# zigbee mapping from long to short object dict +zigbeeLongShortAddr = dict() +zigbeeLongShortAddrMutex = Lock() + +# zigbee mapping from a sequence number to a client +# for correct response handling +zigbeeSeqNumberToClient = dict() +zigbeeSeqNumberToClientMutex = Lock() + +zigeeBindRequest = dict() +zigeeBindRequestMutex = Lock() + +# Keeps record of where to send callbacks to when an HA message is received +zibeeHACallback = dict() +zibeeHACallbackMutex = Lock() + + +# Keeps a record of device addresses whose short addresses have not been +# determined yet +zigbeeUnregisteredAddresses = [] +zigbeeUnregisteredAddressesMutex = Lock() + +# used to signal all threads to end +doEndFlag = False + + +# 2 sockets, one for sending (not bound to a port manually) +# and one for receiving, known port binding by application +# both UDP sockets +sendSoceket = socket(AF_INET, SOCK_DGRAM) +receiveSoceket = socket(AF_INET, SOCK_DGRAM) + + +# zigbee address authority list +zigbeeAddressAuthorityDict = dict() + +# made by changwoo +seqNumberForNotification = dict() + +# ----------------------------------------------------------------------------- +# Helper Methods +# ----------------------------------------------------------------------------- +def reverseShortAddress(shortAddr): + result = shortAddr[len(shortAddr)/2:]+shortAddr[0:len(shortAddr)/2] + return result + +def parseCommandLineArgs(argv): + global ZIGBEE_SERIAL_PORT + global ZIGBEE_SERIAL_BAUD + try: + opts, args = getopt.getopt( + argv, "hp:b:u:", ["port=", "baud=", "udpport="]) + + except getopt.GetoptError: + print 'test.py -p -b -u ' + sys.exit(2) + + for opt, arg in opts: + if opt == '-h': + print 'test.py -p -b -u ' + sys.exit() + + elif opt in ("-p", "--port"): + ZIGBEE_SERIAL_PORT = arg + + elif opt in ("-b", "--baud"): + try: + ZIGBEE_SERIAL_BAUD = int(arg) + except ValueError: + print "Buad rate must be an integer" + sys.exit() + + +# ------------- +# Convenience (Stateless) +# ------------- + +def hexListToChar(hexList): + ''' Method to convert a list/string of characters into their corresponding values + + hexList -- list or string of hex characters + ''' + retString = "" + for h in hexList: + retString += chr(int(h, 16)) + return retString + +def splitByN(seq, n): + ''' Method to split a string into groups of n characters + + seq -- string + n -- group by number + ''' + return [seq[i:i+n] for i in range(0, len(seq), n)] + +def changeEndian(hexString): + ''' Method to change endian of a hex string + + hexList -- string of hex characters + ''' + split = splitByN(hexString, 2) # get each byte + split.reverse(); # reverse ordering of the bytes + + # reconstruct + retString = '' + for s in split: + retString += s + return retString + +def printMessageData(data): + ''' Method to print a zigbee message to the console + + data -- pre-parsed zigbee message + ''' + for d in data: + print d, ' : ', + for e in data[d]: + print "{0:02x}".format(ord(e)), + if (d == 'id'): + print "({})".format(data[d]), + print + +def hexStringToZigbeeHexString(hexString): + ''' Method to change a hex string to a string of characters with the hex values + + hexList -- string of hex characters + ''' + return hexListToChar(splitByN(hexString, 2)) + +def zigbeeHexStringToHexString(zigbeeHexString): + ''' Method to change string of characters with the hex values to a hex string + + hexList -- string of characters with hex values + ''' + + retString = '' + for e in zigbeeHexString: + retString += "{0:02x}".format(ord(e)) + return retString + +def zclDataTypeToBytes(zclPayload): + ''' Method to determine data length of a zcl attribute + + zclPayload -- ZCL payload data, must have dataType as first byte + ''' + attrType = ord(zclPayload[0]) + + if(attrType == 0x00): + return 0 + elif (attrType == 0x08): + return 1 + elif (attrType == 0x09): + return 2 + elif (attrType == 0x0a): + return 3 + elif (attrType == 0x0b): + return 4 + elif (attrType == 0x0c): + return 5 + elif (attrType == 0x0d): + return 6 + elif (attrType == 0x0e): + return 7 + elif (attrType == 0x0f): + return 8 + elif (attrType == 0x10): + return 1 + elif (attrType == 0x18): + return 1 + elif (attrType == 0x19): + return 2 + elif (attrType == 0x1a): + return 3 + elif (attrType == 0x1b): + return 4 + elif (attrType == 0x1c): + return 5 + elif (attrType == 0x1d): + return 6 + elif (attrType == 0x1e): + return 7 + elif (attrType == 0x1f): + return 8 + elif (attrType == 0x20): + return 1 + elif (attrType == 0x21): + return 2 + elif (attrType == 0x22): + return 3 + elif (attrType == 0x23): + return 4 + elif (attrType == 0x24): + return 5 + elif (attrType == 0x25): + return 6 + elif (attrType == 0x26): + return 7 + elif (attrType == 0x27): + return 8 + elif (attrType == 0x28): + return 1 + elif (attrType == 0x29): + return 2 + elif (attrType == 0x2a): + return 3 + elif (attrType == 0x2b): + return 4 + elif (attrType == 0x2c): + return 5 + elif (attrType == 0x2d): + return 6 + elif (attrType == 0x2e): + return 7 + elif (attrType == 0x2f): + return 8 + elif (attrType == 0x30): + return 1 + elif (attrType == 0x31): + return 2 + elif (attrType == 0x38): + return 2 + elif (attrType == 0x39): + return 4 + elif (attrType == 0x3a): + return 8 + elif (attrType == 0x41): + return ord(zclPayload[1]) + elif (attrType == 0x42): + return ord(zclPayload[1]) + elif (attrType == 0x43): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0x44): + return ord(zclPayload[1]) + (256 * ord(zclPayload[2])) + elif (attrType == 0xe0): + return 4 + elif (attrType == 0xe1): + return 4 + elif (attrType == 0xe2): + return 4 + elif (attrType == 0xe8): + return 2 + elif (attrType == 0xe9): + return 2 + elif (attrType == 0xea): + return 4 + elif (attrType == 0xf0): + return 8 + elif (attrType == 0xf1): + return 16 + elif (attrType == 0xff): + return 0 + +# ------------- +# Other +# ------------- + +def createSequenceNumberForClient(addr, packetId): + ''' Method to get and store a sequence number with a specific client + for a specific message. + + addr -- UDP address of the client to associate with the seq. number + packetId -- packet id from the UDP packet + ''' + # keep trying to find a number to return + while(True): + + # get the current time + epoch_time = int(time.time()) + + # get the current list of used numbers + zigbeeSeqNumberToClientMutex.acquire() + keysList = zigbeeSeqNumberToClient.keys() + zigbeeSeqNumberToClientMutex.release() + + # if all the numbers are already used + if(len(keysList) == 256): + + # get a list of all the items + zigbeeSeqNumberToClientMutex.acquire() + itemsList = zigbeeSeqNumberToClient.items() + zigbeeSeqNumberToClientMutex.release() + + # search for a number that is old enough to get rid of otherwise use -1 + seqNum = -1 + for item in itemsList: + if((epoch_time - item[1][1]) > ZIGBEE_SEQUENCE_NUMBER_CLEAR_TIME_SEC): + seqNumber = item[0] + break + + if(seqNum != -1): + # replace the record with new data if we found one to replace + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[seqNumber] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + + return seqNumber + + else: + # not all numbers used yet so pick one randomly + randNum = random.randrange(0,256) + + # check if we are using the number yet + if(randNum not in keysList): + + # we are not so insert to keep track who this number is for and return it + zigbeeSeqNumberToClientMutex.acquire() + zigbeeSeqNumberToClient[randNum] = (addr, epoch_time, packetId) + zigbeeSeqNumberToClientMutex.release() + return randNum + +def getConnectedRadioLongAddress(): + """ Method to make sure we get the MAC address of our local radio""" + global zigbeeConnection + global zigbeeMutex + + # keep looping until we get both the MSBs and the LSBs + while ((not didGetLocalRadioHighAddress) or (not didGetLocalRadioLowAddress)): + + # reissue requests + zigbeeConnection.send('at', command="SH") + zigbeeConnection.send('at', command="SL") + + # sleep for a bit to give the radio time to respond before we check again + time.sleep(2) + +def addressUpdateWorkerMethod(): + ''' Method to keep refreshing the short addresses of the known zigbee devices''' + global doEndFlag + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global zigbeeConnectionMutex + global zigbeeConnection + + # keep looping until signaled to quit + while(not doEndFlag): + + addrList = [] + + # add unregistered (short addresses unknown) devices so + # that we can look them up + zigbeeUnregisteredAddressesMutex.acquire() + addrList.extend(zigbeeUnregisteredAddresses) + zigbeeUnregisteredAddressesMutex.release() + + # add the devices that we have short addresses for so we can + # get their most recent short addresses + zigbeeLongShortAddrMutex.acquire() + addrList.extend(zigbeeLongShortAddr.keys()) + zigbeeLongShortAddrMutex.release() + + # Loop through all the addresses and send messages for each address + for ad in addrList: + + # create payload for a query on the network for a short address + payload = '\x00' + payload += hexStringToZigbeeHexString(changeEndian(ad)) + payload += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + dest_addr_long=hexStringToZigbeeHexString(ad), + dest_addr='\xff\xfd', + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x00', + profile='\x00\x00', + data=payload + ) + zigbeeConnectionMutex.release() + + time.sleep(8) + + +# ------------- +# UDP +# ------------- + +def sendUdpSuccessFail(addr, packetTypeStr, packetIdStr, sucOrFail, reason=None): + ''' Method to send a success or fail back to a client. + + addr -- UDP address to send packet to + packetTypeStr -- name of this specific packet + packetIdStr -- packet id to send + sucOrFail -- whether this is a success or fail message (True = success) + reason -- reason of failure (if needed, default is None) + + ''' + + global sendSoceket + + # construct the message + message = "type: " + packetTypeStr.strip() + "\n" + message += "packet_id: " + packetIdStr + "\n" + + if(sucOrFail): + message += "response: success \n" + else: + message += "response: fail \n" + message += "reason: " + reason + "\n" + + # send message in a UDP packet + sendSoceket.sendto(message,addr) + +def processUdpZdoBindReqMessage(parsedData, addr): + + shortAddr = None + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'out_of_space') + return + + # a bind request was made so must store and wait for response + # before we setup callbacks, so keep just the data we need to create the callback + zigeeBindRequestMutex.acquire() + zigeeBindRequest[seqNumber] = (parsedData['device_address_long'], + parsedData['cluster_id'], + parsedData['packet_id'], + addr) + zigeeBindRequestMutex.release() + + # construct the short and long addresses of the message for sending + # make sure they are in the correct format + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + + # create the payload data + payloadData = "" + payloadData += chr(seqNumber) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_address_long'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['device_endpoint'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['cluster_id'])) + payloadData += '\x03' + payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS)) + payloadData += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x21', + profile='\x00\x00', + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + # send a failure packet since there is no short address available + sendUdpSuccessFail(addr, 'zdo_bind_request', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpZdoUnBindReqMessage(parsedData, addr): + zibeeHACallbackMutex.acquire(); + if(zibeeHACallback.has_key(parsedData['device_address_long'], parsedData['cluster_id'])): + zibeeHACallback(parsedData['device_address_long'], parsedData['cluster_id']).remove(addr) + zibeeHACallbackMutex.release() + sendUdpSuccessFail(addr, 'zdo_unbind_request', parsedData['packet_id'], True) + + + +def processUdpSendAddressMessage(parsedData, addr): + ''' Method handle a send address command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigbeeUnregisteredAddresses + global zigbeeUnregisteredAddressesMutex + global sendSoceket + + print "process send address" + + + # construct success message + message = "type: send_address_response\n" + message += "packet_id: " + parsedData['packet_id'] + "\n" + message += "response: success\n" + + # tell client that we got their request + sendSoceket.sendto(message,addr) + print "responding", message + + # construct + zigbeeLongShortAddrMutex.acquire() + doesHaveKey = zigbeeLongShortAddr.has_key(parsedData['device_address_long']) + zigbeeLongShortAddrMutex.release() + + if(doesHaveKey): + # long address is already registered with the system so no need to do anything + return + + # long address not registered so add it for short address lookup + zigbeeUnregisteredAddressesMutex.acquire() + zigbeeUnregisteredAddresses.append(parsedData['device_address_long']) + zigbeeUnregisteredAddressesMutex.release() + + + +#made by changwoo +def processUdpEnrollmentResponse(parsedData, addr): + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + + # create the payload data + payloadData = "" + payloadData += '\x01' + payloadData += chr(seqNumber) + payloadData += '\x00' + payloadData += '\x00\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x40', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x01', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + print '> EnrollmentResponse is sent' + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_enrollment_response', parsedData['packet_id'], False, 'short_address_unknown') + pass + + + + +#made by changwoo +def processUdpZclWriteAttributesMessage(parsedData, addr): + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x02' + payloadData += '\x10\x00' + payloadData += '\xF0' +# payloadData += '\xDA\x9A\xD9\x40\x00\xA2\x13\x00' + payloadData += hexStringToZigbeeHexString(changeEndian(ZIGBEE_DEVICE_ADDRESS)) + + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x08', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x01', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + + print '' + print '> WriteAttributesReq is sent : '+str(shortAddr) + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_write_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + +#made by changwoo +def processUdpZclChangeSwitchReqMessage(parsedData, addr): + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'change_switch_request', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + value = hexStringToZigbeeHexString(parsedData['value']) + + # create and send binding command + zigbeeConnectionMutex.acquire() + + zigbeeConnection.send('tx_explicit', + frame_id='\x40', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x01', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data='\x01'+chr(seqNumber)+value + ) + time.sleep(1) + if parsedData['value']==1: + print '> The outlet sensor turned on' + else : + print '> The outlet sensor turned off' + + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + + + +# made by changwoo +def processUdpBroadcastingRouteRecordReqMessage(parsedData, addr): + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'broadcast_route_record_request', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # create and send binding command + zigbeeConnectionMutex.acquire() + + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long='\x00\x00\x00\x00\x00\x00\xff\xff', + dest_addr='\xff\xfe', + src_endpoint='\x00', + dest_endpoint=dstEndpoint, + cluster='\x00\x32', + profile='\x00\x00', + data='\x12'+'\x01' + ) + time.sleep(1) + print '> BroadcastingRouteRecordReq is sent' + + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + + +#made by changwoo +def processUdpManagementPermitJoiningReqMessage(parsedData, addr): + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + global matchDescriptorReqSingleton + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + + # create the payload data + payloadData = "" + payloadData += chr(seqNumber) + payloadData += '\x5a' + payloadData += '\x00' + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint='\x00', + cluster=clusterId, + profile='\x00\x00', + data=payloadData + ) + print '> ManagementPermitJoiningReq is sent' + + #stop answering 0x6 + matchDescriptorReqSingleton= False + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'management_permit_joining_request', parsedData['packet_id'], False, 'short_address_unknown') + pass + + +def processUdpZclReadAttributesMessage(parsedData, addr): + ''' Method handle a ZCL read attribute command + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + + # send an error message, could not get a sequence number to use at this time + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'out_of_space') + return + + # get the info for sending + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # get all the attributes + attributeIds = parsedData['attribute_ids'].split(',') + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x00' + + # make all the attributes payloads + for attr in attributeIds: + attr = attr.strip() + attr = changeEndian(attr) + payloadData += hexStringToZigbeeHexString(attr) + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + # send a fail response + sendUdpSuccessFail(addr, 'zcl_read_attributes', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpZclConfigureReportingMessage(parsedData, addr): + ''' Method handle a zcl configure reporting message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + + global zigbeeLongShortAddr + global zigbeeLongShortAddrMutex + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + + if(zigbeeAddressAuthorityDict.has_key(addr)): + l = zigbeeAddressAuthorityDict[addr] + if(parsedData['device_address_long'] not in l): + return + else: + return + + + shortAddr = None + + # get the short address for this device long address if possible + zigbeeLongShortAddrMutex.acquire() + if(zigbeeLongShortAddr.has_key(parsedData['device_address_long'])): + shortAddr = zigbeeLongShortAddr[parsedData['device_address_long']] + zigbeeLongShortAddrMutex.release() + + # if there is a short address than we can send the message + # if there is not one then we cannot since we need both the short and + # the long address + if(shortAddr != None): + + # get a request number + seqNumber = createSequenceNumberForClient(addr, parsedData['packet_id']) + + # send back failure + if(seqNumber == -1): + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'out_of_space') + return + + destLongAddr = hexStringToZigbeeHexString(parsedData['device_address_long']) + destShortAddr = hexStringToZigbeeHexString(shortAddr) + profileId = hexStringToZigbeeHexString(parsedData['profile_id']) + clusterId = hexStringToZigbeeHexString(parsedData['cluster_id']) + dstEndpoint = hexStringToZigbeeHexString(parsedData['device_endpoint']) + + # create the payload data + payloadData = "" + payloadData += '\x00' + payloadData += chr(seqNumber) + payloadData += '\x06' + payloadData += '\x00' + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['attribute_id'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['data_type'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['min_reporting_interval'])) + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['max_reporting_interval'])) + + if(parsedData.has_key('reportable_change')): + payloadData += hexStringToZigbeeHexString(changeEndian(parsedData['reportable_change'])) + + + # create and send binding command + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + # frame_id=chr(seqNumber), + dest_addr_long=destLongAddr, + dest_addr=destShortAddr, + src_endpoint='\x00', + dest_endpoint=dstEndpoint, + cluster=clusterId, + profile=profileId, + data=payloadData + ) + zigbeeConnectionMutex.release() + + + else: + sendUdpSuccessFail(addr, 'zcl_configure_reporting', parsedData['packet_id'], False, 'short_address_unknown') + pass + +def processUdpPolicySet(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Policy set: ", parsedData + print 'addr : ', addr + + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS : #or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3 : + key = (parsedData['ip_address'], int(parsedData['port'])) + if (zigbeeAddressAuthorityDict.has_key(key)): + zigbeeAddressAuthorityDict[key].append(parsedData['device_address_long']) + else: + zigbeeAddressAuthorityDict[key] = [parsedData['device_address_long']] + + +def processUdpPolicyClear(parsedData, addr): + ''' Method handle a policy set message + + parsedData -- Pre-parsed Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + print "==================================================================" + print "Clear policy: ", parsedData + + # do nothing if wrong source + if addr == SYSTEM_MASTER_ADDRESS : #or addr == SYSTEM_MASTER_ADDRESS2 or addr == SYSTEM_MASTER_ADDRESS3: + zigbeeAddressAuthorityDict.clear() + + +# ------------- +# Zigbee +# ------------- + +def processZigbeeATCommandMessage(parsedData): + ''' Method to process an AT zigbee message + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + global ZIGBEE_DEVICE_ADDRESS + global didGetLocalRadioHighAddress + global didGetLocalRadioLowAddress + + # command response for the high bytes of the local device long address + if(parsedData['command'] == 'SH'): + # convert the parameter to a string value (human readable) + value = "" + for e in parsedData['parameter']: + value += "{0:02x}".format(ord(e)) + + # set the correct portion of the address + ZIGBEE_DEVICE_ADDRESS = value + ZIGBEE_DEVICE_ADDRESS[8:] + + #signal that we got this part of the address + didGetLocalRadioHighAddress = True + + # command response for the low bytes of the local device long address + elif(parsedData['command'] == 'SL'): + # convert the parameter to a string value (human readable) + value = "" + for e in parsedData['parameter']: + value += "{0:02x}".format(ord(e)) + + # set the correct portion of the address + ZIGBEE_DEVICE_ADDRESS = ZIGBEE_DEVICE_ADDRESS[0:8] + value + + #signal that we got this part of the address + didGetLocalRadioLowAddress = True + +def processZigbeeRxExplicitCommandMessage(parsedData): + ''' Method to process a rx-explicit zigbee message + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + global zigeeBindRequestMutex + global zigeeBindRequest + global zigbeeConnectionMutex + global zigbeeConnection + global ManagementPermitJoiningReqSuccess + + # get the long and short addresses from the message payload since we can + # use these to update the short addresses since this short address is fresh + longAddr = zigbeeHexStringToHexString(parsedData['source_addr_long']) + shortAddr = zigbeeHexStringToHexString(parsedData['source_addr']) + + # check if this short address is for a device that has yet to be + # registered + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # update/ or insert the short address + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + global matchDescriptorReqSingleton + global deviceAnnouncementSingleton + global seqNumberForNotification + + # if this is a ZDO message/response + if(parsedData['profile'] == '\x00\x00'): + + # mabe by changwoo + # if this is a Match Descriptor Request so we need to answer. + if(parsedData['cluster'] == '\x00\x06' and matchDescriptorReqSingleton): + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x08', + # frame_id=chr(seqNumber), + dest_addr_long=parsedData['source_addr_long'], + dest_addr=parsedData['source_addr'], + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x06', + profile='\x00\x00', + data=parsedData['rf_data'] + ) + time.sleep(1) + zigbeeConnection.send('tx_explicit', + frame_id='\x40', + # frame_id=chr(seqNumber), + dest_addr_long=parsedData['source_addr_long'], + dest_addr=parsedData['source_addr'], + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x80\x06', + profile='\x00\x00', + data=parsedData['rf_data'][0]+ '\x00\x00\x00' + '\x01\x01' + ) + time.sleep(1) + print '' + print '[ 0x0006 ] Match Descriptor Request - answered' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + zigbeeConnectionMutex.release() + + + # if this is a device announcement so we can get some useful data from it + elif(parsedData['cluster'] == '\x00\x13' and deviceAnnouncementSingleton): + + # pick out the correct parts of the payload + longAddr = zigbeeHexStringToHexString(parsedData['rf_data'][3:11]) + shortAddr = zigbeeHexStringToHexString(parsedData['rf_data'][1:3]) + + # change the endian of the address + longAddr = changeEndian(longAddr) + shortAddr = changeEndian(shortAddr) + + # update the table with the new information + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + # check if this short address is for a device that has yet to be + # registered + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + + # made by changwoo + zigbeeConnectionMutex.acquire() + zigbeeConnection.send('tx_explicit', + frame_id='\x08', + # frame_id=chr(seqNumber), + dest_addr_long=parsedData['source_addr_long'], + dest_addr=parsedData['source_addr'], + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x13', + profile='\x00\x00', + data=parsedData['rf_data'] + ) + print '' + print '[ 0x0013 ] device announcement - answered' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + deviceAnnouncementSingleton = False + zigbeeConnectionMutex.release() + + + # if this is a response to a zdo bind_req message + elif(parsedData['cluster'] == '\x80\x21'): + + # get the status and sequence number from the message + seqNumber = parsedData['rf_data'][0] + statusCode = parsedData['rf_data'][1] + + # get the bind tuple information + # for this specific bind request + tup = None + zigeeBindRequestMutex.acquire() + if(zigeeBindRequest.has_key(ord(seqNumber))): + tup = zigeeBindRequest[ord(seqNumber)] + zigeeBindRequestMutex.release() + + if(tup == None): + # cant really do anything in this case... + # don't have any information on who the data is for + return + + # successful binding + if(ord(statusCode) == 0): + + # add a callback for this specific device and cluster + # to the HA callback dict + zibeeHACallbackMutex.acquire(); + if(zibeeHACallback.has_key((tup[0], tup[1]))): + if(tup[3] not in zibeeHACallback[(tup[0], tup[1])]): + zibeeHACallback[(tup[0], tup[1])].append(tup[3]) + else: + zibeeHACallback[(tup[0], tup[1])] = [tup[3]] + zibeeHACallbackMutex.release() + + # send success message + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], True) + + # Not Supported + elif (ord(statusCode) == 170): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'not_supported') + + # Table Full + elif (ord(statusCode) == 174): + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'table_full') + + # Other issue, dont have code for + else: + sendUdpSuccessFail(tup[3], 'zdo_bind_request', tup[2], False, 'other') + + # if this is a response to a short address query + elif(parsedData['cluster'] == '\x80\x00'): + + # get a status code + statusCode = parsedData['rf_data'][0] + + # does not matter if this is not a success, we can try again later + if(statusCode != '\x00'): + # status code was not success so do not do anything + return + + # get the short and long address information + longAddr = changeEndian(zigbeeHexStringToHexString(parsedData['rf_data'][2:10])) + shortAddr = changeEndian(zigbeeHexStringToHexString( parsedData['rf_data'][10:12])) + + # remove device from list of unregistered devices if it is in it + zigbeeUnregisteredAddressesMutex.acquire() + if(longAddr in zigbeeUnregisteredAddresses): + zigbeeUnregisteredAddresses.remove(longAddr) + zigbeeUnregisteredAddressesMutex.release() + + # update/insert the short address + zigbeeLongShortAddrMutex.acquire() + zigbeeLongShortAddr[longAddr] = shortAddr + zigbeeLongShortAddrMutex.release() + + #made by changwoo + elif(parsedData['cluster'] == '\x80\x06'): + print '' + print '[ 0x8006 ] get Match Descriptor Response' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + + #made by changwoo + elif(parsedData['cluster'] == '\x80\x36'): + print '' + print '[ 0x8036 ] get Management Permit Joining Response' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + + ManagementPermitJoiningReqSuccess = True + + #made by changwoo + else : + print '' + print '[ '+zigbeeHexStringToHexString(parsedData['cluster'])+' ] ...' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + + + # if this is a home automation zcl message/response + elif (parsedData['profile'] == '\x01\x04'): + + # get the zcl message header + zclFrameControl = parsedData['rf_data'][0] + zclSeqNumber = parsedData['rf_data'][1] + zclCommand = parsedData['rf_data'][2] + zclStatus = parsedData['rf_data'][3] + + #made by changwoo + if(zclCommand == '\x00'): + print '' + print '> ('+zigbeeHexStringToHexString(zclStatus)+') notification! : '+ zigbeeHexStringToHexString( parsedData['rf_data'] ) + + # find who to send response + tup = None + zigbeeSeqNumberToClientMutex.acquire() + + if(longAddr in seqNumberForNotification): + key = longAddr + if(zigbeeSeqNumberToClient.has_key(seqNumberForNotification[key])): + tup = zigbeeSeqNumberToClient[seqNumberForNotification[key]] + #del zigbeeSeqNumberToClient[seqNumberForNotification] # don't delete. + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + # create the response message + packetId = tup[2] + message = "type : zcl_zone_status_change_notification\n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "status: " + zigbeeHexStringToHexString(zclStatus) + "\n" + message += "attributes: success" + message += "\n" + # send the socket + sendSoceket.sendto(message,tup[0]) + print(">port : ", tup[0][1]) + + + + # this is a zcl read attribute response + elif(zclCommand == '\x01'): + + # get the zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # get the data for each data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + attributeStatus = zclPayload[2] + zclPayload = zclPayload[3:] + + if(ord(attributeStatus) != 0): + # if attribute is not supported then it has no data + # package the data and add it to the list + attibuteResponseList.append((attributeId,"not_supported")) + else: + + # get the data type and data length of the attributre + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + # consume zcl payload data + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + # package the data and add it to the list + newData = (attributeId,"success", attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + + # consume the data size of the payload + zclPayload = zclPayload[dataLength:] + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # create the response message + packetId = tup[2] + message = "type : zcl_read_attributes_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # create the message for each attribute + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + if(t[1] == "success"): + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "success" + message += ", " + message += "%0.2x" % ord(t[2]) + message += ", " + + dat = "" + for c in (t[3]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + else: + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "not_supported" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + # send the socket + sendSoceket.sendto(message,tup[0]) + + + + + # made by changwoo + # this is a zcl write attribute response + elif(zclCommand == '\x04'): + + # get the zcl payload + zclPayload = parsedData['rf_data'][3] + # the response is '70' which means already resister the mac address or 'success', then let JAVA knows it + if(zclStatus == '\x70' or zclPayload == '\x00'): + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + seqNumberForNotification[longAddr] = ord(zclSeqNumber) + #del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # create the response message + packetId = tup[2] + message = "type : zcl_write_attributes_response\n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: success" + message += "\n" + # send the socket + sendSoceket.sendto(message,tup[0]) + print '' + print '[ 0x0500 ] get Write Attribute Response success' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + + else: + print '' + print '[ 0x0500 ] get Write Attribute Response' + print '> rfdata : '+zigbeeHexStringToHexString(parsedData['rf_data']) + + + + # this is a zcl configure attribute response + elif(zclCommand == '\x07'): + + # find who to send response to + tup = None + zigbeeSeqNumberToClientMutex.acquire() + if(zigbeeSeqNumberToClient.has_key(ord(zclSeqNumber))): + tup = zigbeeSeqNumberToClient[ord(zclSeqNumber)] + del zigbeeSeqNumberToClient[ord(zclSeqNumber)] + zigbeeSeqNumberToClientMutex.release() + + # no one to send the response to so just move on + if(tup == None): + # cant really do anything here + return + + # get zcl payload + zclPayload = parsedData['rf_data'][3:] + + # construct the message + packetId = tup[2] + message = "type : zcl_configure_reporting_response \n" + message += "packet_id: " + packetId + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + if(len(zclPayload) == 1): + # if all the configurations are a success then only send back a success + # based on zigbee specs + message += "all_success \n"; + sendSoceket.sendto(message,tup[0]) + + else: + attibuteResponseList = [] + + # get each attributes data + while(len(zclPayload) > 0): + attributeStatus = zclPayload[0] + attributeDirection = zclPayload[1] + attributeId = zclPayload[2:4] + zclPayload = zclPayload[4:] + + newData = (attributeStatus,attributeDirection, attributeId) + attibuteResponseList.append(newData) + + # package each attribute + for t in attibuteResponseList: + attrId = ord(t[2][0]) + (256 * ord(t[2][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + if(ord(t[0]) == 0): + message += "success" + else: + message += "error" + + message += ", " + + if(ord(t[1]) == 0): + message += "reported" + else: + message += "received" + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + sendSoceket.sendto(message,tup[0]) + + # this is a zcl report attribute message + elif(zclCommand == '\x0a'): + print "get Report attribute " + # get teh zcl payload + zclPayload = parsedData['rf_data'][3:] + attibuteResponseList = [] + + # extract the attribute data + while(len(zclPayload) > 0): + attributeId = zclPayload[0:2] + zclPayload = zclPayload[2:] + attributeType = zclPayload[0] + dataLength = zclDataTypeToBytes(zclPayload) + + if ((ord(attributeType) == 0x41) or (ord(attributeType) == 0x42)): + zclPayload = zclPayload[2:] + elif ((ord(attributeType) == 0x43) or (ord(attributeType) == 0x44)): + zclPayload = zclPayload[3:] + else: + zclPayload = zclPayload[1:] + + newData = (attributeId, attributeType ,zclPayload[0:dataLength]) + attibuteResponseList.append(newData) + zclPayload = zclPayload[dataLength:] + + + # get callback clients to respond to + callbackIndex = (zigbeeHexStringToHexString(parsedData['source_addr_long']), zigbeeHexStringToHexString(parsedData['cluster'])) + retAddr = None + zibeeHACallbackMutex.acquire() + if(zibeeHACallback.has_key(callbackIndex)): + retAddr = zibeeHACallback[callbackIndex] + zibeeHACallbackMutex.release() + + # no one to respond to so do nothing here + if(retAddr == None): + return + + # construct the message + message = "type : zcl_report_attributes \n" + message += "packet_id: " + ("%0.2x" % ord(zclSeqNumber)) + "\n" + message += "cluster_id: " + zigbeeHexStringToHexString(parsedData['cluster']) + "\n" + message += "profile_id: " + zigbeeHexStringToHexString(parsedData['profile']) + "\n" + message += "attributes: " + + # package the attributes + for t in attibuteResponseList: + attrId = ord(t[0][0]) + (256 * ord(t[0][1])) + attrIdStr = "%0.4x" % attrId + attrIdStr = changeEndian(attrIdStr) + + message += attrIdStr + message += ", " + message += "%0.2x" % ord(t[1]) + message += ", " + + dat = "" + for c in (t[2]): + dat += "%0.2x" % ord(c) + dat = changeEndian(dat) + message += dat + message += ";" + + message = message[0:len(message) - 1] + message += "\n" + print "Sending", message + + # send to all client that want this callback + for ra in retAddr: + sendSoceket.sendto(message,ra) + +# ----------------------------------------------------------------------------- +# Communication Callback/Parse Methods +# ----------------------------------------------------------------------------- +def handleNewZigbeeMessage(parsedData): + ''' Method to process a zigbee message from the local radio. + + parsedData -- Pre-parsed (into a dict) data from message. + ''' + #print "==================================================================" + #print '' + #print "New Zigbee Message" + #printMessageData(parsedData) + + # dispatch to the correct zigbee handler + if (parsedData['id'] == 'at_response'): + processZigbeeATCommandMessage(parsedData) + + elif (parsedData['id'] == 'rx_explicit'): + processZigbeeRxExplicitCommandMessage(parsedData) + + else: + print "Unknown API format" + + #print "==================================================================" + + + +def handleNewUdpPacket(data, addr): + ''' Method to parse and handle an incoming UDP packet. + + data -- Data that was in the UDP packet. + addr -- Address (IP and Port) of the UDP packet origin. + ''' + global ManagementPermitJoiningReqSuccess + + #print "==================================================================" + #print '' + #print "Got New UDP packet..." + #print data + + + # data comes in as 'key: value\n key: value...' string and so needs to be + # parsed into a dict + parsedData = dict() + + # 1 key, value pair per line + for line in data.split('\n'): + + # key and values are split based on a ':' + fields = line.split(':') + + # make sure properly formated otherwise just ignore it + if len(fields) == 2: + + # do strips to remove any white spacing that may have resulted + # from improper packing on the sender side + parsedData[fields[0].strip()] = fields[1].strip() + + + # wrap in try statement just in case there is an improperly formated packet we + # can deal with it + try: + # dispatch to the correct process method + if(parsedData["type"] == "zdo_bind_request"): + processUdpZdoBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "zdo_unbind_request"): + processUdpZdoUnBindReqMessage(parsedData, addr) + elif(parsedData["type"] == "send_address"): + processUdpSendAddressMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_read_attributes"): + processUdpZclReadAttributesMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_configure_reporting"): + processUdpZclConfigureReportingMessage(parsedData, addr) + elif(parsedData["type"] == "policy_set"): + processUdpPolicySet(parsedData, addr) + elif(parsedData["type"] == "policy_clear"): + processUdpPolicyClear(parsedData, addr) + elif(parsedData["type"] == "management_permit_joining_request"): #made by changwoo + processUdpManagementPermitJoiningReqMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_write_attributes" and ManagementPermitJoiningReqSuccess): #made by changwoo + processUdpZclWriteAttributesMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_enrollment_response"): #made by changwoo + processUdpEnrollmentResponse(parsedData, addr) + elif(parsedData["type"] == "zdo_broadcast_route_record_request"): #made by changwoo + processUdpBroadcastingRouteRecordReqMessage(parsedData, addr) + elif(parsedData["type"] == "zcl_change_switch_request"): #made by changwoo + processUdpZclChangeSwitchReqMessage(parsedData, addr) + else: + #print "unknown Packet: " + parsedData["type"] + pass + except: + # if we ever get here then something went wrong and so just ignore this + # packet and try again later + print "I didn't expect this error:", sys.exc_info()[0] + traceback.print_exc() + + #print "==================================================================" + + +# ----------------------------------------------------------------------------- +# Main Running Methods +# ----------------------------------------------------------------------------- + +def main(): + '''Main function used for starting the application as the main driver''' + + global ZIGBEE_SERIAL_PORT + global ZIGBEE_SERIAL_BAUD + global UDP_RECEIVE_PORT + global zigbeeConnection + global zigbeeMutex + global doEndFlag + + parseCommandLineArgs(sys.argv[1:]) + + # create serial object used for communication to the zigbee radio + sc = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD) + + # create a zigbee object that handles all zigbee communication + # we use this to do all communication to and from the radio + # when data comes from the radio it will get a bit of unpacking + # and then a call to the callback specified will be done with the + # unpacked data + zigbeeConnection = ZigBee(sc, callback=handleNewZigbeeMessage) + + # get the long address of our local radio before we start doing anything + getConnectedRadioLongAddress(); + + # setup incoming UDP socket and bind it to self and specified UDP port + # sending socket does not need to be bound to anything + receiveSoceket.bind(('127.0.0.1', UDP_RECEIVE_PORT)) + + # create the thread that does short address lookups + addressUpdateWorkerThread = threading.Thread(target=addressUpdateWorkerMethod) + addressUpdateWorkerThread.start() + + try: + # Main running loop + while(True): + #print "==================================================================" + #print '' + #print "Waiting..." + #print "==================================================================" + + # wait for an incoming UDP packet + # this is a blocking call + data, addr = receiveSoceket.recvfrom(4096) + + # handle the UDP packet appropriately + handleNewUdpPacket(data, addr) + + except KeyboardInterrupt: + # use the keyboard interupt to catch a ctrl-c and kill the application + pass + + except: + # something went really wrong and so exit with error message + traceback.print_exc() + + # signal all threads to exit + doEndFlag = True + + # wait for threads to finish before closing of the resources + addressUpdateWorkerThread.join() + + + # make sure to close all the connections + zigbeeConnection.halt() + receiveSoceket.close() + sendSoceket.close() + +if __name__ == "__main__": + # call main function since this is being run as the start + main() diff --git a/benchmarks/other/XbeePythonDriver/xbee_test.py b/benchmarks/other/XbeePythonDriver/xbee_test.py new file mode 100644 index 0000000..9b31efa --- /dev/null +++ b/benchmarks/other/XbeePythonDriver/xbee_test.py @@ -0,0 +1,211 @@ + +from xbee import ZigBee +import serial +import time +import collections +import sys +import getopt +from socket import * +import traceback +from threading import Thread, Lock +import random + +# Communication Parameters +ZIGBEE_SERIAL_PORT = "/dev/cu.usbserial-DN01DJIP" +ZIGBEE_SERIAL_BAUD = 115200 +UDP_RECEIVE_PORT = 5005 + +# Radio Parameters +ZIGBEE_DEVICE_ADDRESS = "0000000000000000" +didzigbeeAddressLSBs = False +didzigbeeAddressMSBs = False + + +def parseCommandLineArgs(argv): + global ZIGBEE_SERIAL_PORT + global ZIGBEE_SERIAL_BAUD + try: + opts, args = getopt.getopt( + argv, "hp:b:u:", ["port=", "baud=", "udpport="]) + + except getopt.GetoptError: + print 'test.py -p -b -u ' + sys.exit(2) + + for opt, arg in opts: + if opt == '-h': + print 'test.py -i -o ' + sys.exit() + + elif opt in ("-p", "--port"): + ZIGBEE_SERIAL_PORT = arg + + elif opt in ("-b", "--baud"): + try: + ZIGBEE_SERIAL_BAUD = int(arg) + except ValueError: + print "Buad rate must be an integer" + sys.exit() + + # elif opt in ("-u", "--udpport"): + # try: + # UDP_PORT = int(arg) + # except ValueError: + # print "Udp port must be an integer" + # sys.exit() + + +def printMessageData(data): + for d in data: + print d, ' : ', + for e in data[d]: + print "{0:02x}".format(ord(e)), + if (d == 'id'): + print "({})".format(data[d]), + print + + +def convertMessageToString(data): + retString = "" + + for d in data: + retString += d + ' : ' + + for e in data[d]: + retString += "{0:02x}".format(ord(e)) + + if (d == 'id'): + retString += "({})".format(data[d]) + + retString += "\n" + + return retString + + +def splitByN(seq, n): + """A generator to divide a sequence into chunks of n units.""" + while seq: + yield seq[:n] + seq = seq[n:] + + +def hexListToChar(hexList): + retString = "" + for h in hexList: + retString += chr(int(h, 16)) + return retString + + + + +def zigbeeMessageCallbackHandler(data): + global ZIGBEE_DEVICE_ADDRESS + global didzigbeeAddressLSBs + global didzigbeeAddressMSBs + + print "================================================================================" + printMessageData(data) + print "================================================================================" + + + +if __name__ == "__main__": + + # zigbeeClientCallbackDict["000d6f0003ebf2ee"] = [("127.0.0.1", 5556)] + + # parse the command line arguments + parseCommandLineArgs(sys.argv[1:]) + + # create serial object used for communication to the zigbee radio + serialConnection = serial.Serial(ZIGBEE_SERIAL_PORT, ZIGBEE_SERIAL_BAUD) + + # create a zigbee object that handles all zigbee communication + # we use this to do all communication to and from the radio + # when data comes from the radio it will get a bit of unpacking + # and then a call to the callback specified will be done with the + # unpacked data + zigbeeConnection = ZigBee( + serialConnection, callback=zigbeeMessageCallbackHandler) + + + + print "Starting main loop..." + try: + # zigbeeConnection.send('tx_explicit', + # frame_id='\x01', + # dest_addr_long='\x00\x0d\x6f\x00\x03\xeb\xf2\xee', + # dest_addr='\xff\xfd', + # src_endpoint='\x00', + # dest_endpoint='\x00', + # cluster='\x00\x00', + # profile='\x00\x00', + # data='\xb4' + '\xee\xf2\xeb\x03\x00\x6f\x0d\x00' + '\x00' + # ) + + # zigbeeConnection.send('tx_explicit', + # frame_id='\x01', + # dest_addr_long='\x00\x0d\x6f\x00\x03\xeb\xf2\xee', + # dest_addr='\xff\xfd', + # src_endpoint='\x00', + # dest_endpoint='\x00', + # cluster='\x00\x21', + # profile='\x00\x00', + # data='\xb4' + '\xee\xf2\xeb\x03\x00\x6f\x0d\x00' + '\x01' + '\x05\x04' + + # '\x03' + '\xda\x9a\xd9\x40\x00\xa2\x13\x00' + '\x00' + # ) + + # zigbeeConnection.send('tx_explicit', + # frame_id='\x01', + # dest_addr_long='\x00\x0d\x6f\x00\x03\xeb\xf2\xee', + # dest_addr='\x65\x04', + # src_endpoint='\x00', + # dest_endpoint='\x01', + # cluster='\x04\x05', + # profile='\x01\x04', + # data='\x00' + '\xa1' + '\x06' + '\x00' + '\x00\x00' + + # '\x21' + '\x01\x00' + '\x3c\x00' + '\x00\x00' + # ) + # time.sleep(0.1) + + + print "looping" + while True: + print "sending" + # zigbeeConnection.send('tx_explicit', + # frame_id='\x01', + # dest_addr_long='\x00\x0d\x6f\x00\x03\xeb\xf2\xee', + # dest_addr='\x9a\xb7', + # src_endpoint='\x00', + # dest_endpoint='\x00', + # cluster='\x00\x00', + # profile='\x00\x00', + # data='\xb4' + '\xee\xf2\xeb\x03\x00\x6f\x0d\x00' + '\x00' + # ) + + + zigbeeConnection.send('tx_explicit', + frame_id='\x01', + dest_addr_long='\x00\x0d\x6f\x00\x03\xeb\xf2\xee', + dest_addr='\x9a\xb7', + src_endpoint='\x00', + dest_endpoint='\x00', + cluster='\x00\x21', + profile='\x00\x00', + data='\xb4' + '\xee\xf2\xeb\x03\x00\x6f\x0d\x00' + '\x01' + '\x05\x04' + + '\x03' + '\xda\x9a\xd9\x40\x00\xa2\x13\x00' + '\x00' + ) + + time.sleep(1) + + except KeyboardInterrupt: + pass + except: + traceback.print_exc() + + + zigbeeConnection.halt() + serialConnection.close() + + + +