Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #! /usr/bin/python
- # -*- coding: UTF-8 -*-
- from xbee import ZigBee
- #from apscheduler.scheduler import Scheduler
- import logging
- import datetime
- import time
- import serial
- import sys
- import shlex
- import sqlite3
- import struct
- from string import *
- #-------------------------------------------------
- # the database where I'm storing stuff
- DATABASE='/root/XBeeIris/IrisSwitches'
- # on the Raspberry Pi the serial port is ttyAMA0
- XBEEPORT = '/dev/ttyAMA0'
- #XBEEPORT = '/dev/ttyUSB0'
- XBEEBAUD_RATE = 9600
- # The XBee addresses I'm dealing with
- BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff'
- UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address
- switchLongAddr = '12'
- switchShortAddr = '12'
- #-------------------------------------------------
- logging.basicConfig()
- # this is the only way I could think of to get the address strings to store.
- # I take the ord() to get a number, convert to hex, then take the 3 to end
- # characters and pad them with zero and finally put the '0x' back on the front
- # I put spaces in between each hex character to make it easier to read. This
- # left an extra space at the end, so I slice it off in the return statement.
- # I hope this makes it easier to grab it out of the database when needed
- def addrToString(funnyAddrString):
- hexified = ''
- for i in funnyAddrString:
- hexified += '0x' + hex(ord(i))[2:].zfill(2) + ' '
- return hexified[:-1]
- def convtohex(binarydata):
- hexdata = ''
- for i in binarydata:
- hexdata += hex(ord(i))[2:].zfill(2) + ' '
- return hexdata[:-1]
- #------------ XBee Stuff -------------------------
- # Open serial port for use by the XBee
- ser = serial.Serial(XBEEPORT, XBEEBAUD_RATE)
- # this is a call back function. When a message
- # comes in this function will get the data
- def messageReceived(data):
- print 'got packet: %s' %(data)
- print "Hex: %s" %(convtohex(data['rf_data']))
- # This is a test program, so use global variables and
- # save the addresses so they can be used later
- global switchLongAddr
- global switchShortAddr
- switchLongAddr = data['source_addr_long']
- switchShortAddr = data['source_addr']
- clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
- print 'Cluster ID:', hex(clusterId),
- if (clusterId == 0x13):
- # This is the device announce message.
- # due to timing problems with the switch itself, I don't
- # respond to this message, I save the response for later after the
- # Match Descriptor request comes in. You'll see it down below.
- # if you want to see the data that came in with this message, just
- # uncomment the 'print data' comment up above
- print 'Device Announce Message'
- elif (clusterId == 0x8005):
- # this is the Active Endpoint Response This message tells you
- # what the device can do, but it isn't constructed correctly to match
- # what the switch can do according to the spec. This is another
- # message that gets it's response after I receive the Match Descriptor
- print 'Active Endpoint Response'
- elif (clusterId == 0x0006):
- # Match Descriptor Request; this is the point where I finally
- # respond to the switch. Several messages are sent to cause the
- # switch to join with the controller at a network level and to cause
- # it to regard this controller as valid.
- #
- # First the Active Endpoint Request
- payload1 = '\x00\x00'
- zb.send('tx_explicit',
- dest_addr_long = switchLongAddr,
- dest_addr = switchShortAddr,
- src_endpoint = '\x00',
- dest_endpoint = '\x00',
- cluster = '\x00\x05',
- profile = '\x00\x00',
- data = payload1
- )
- print 'sent Active Endpoint'
- # Now the Match Descriptor Response
- payload2 = '\x00\x00\x00\x00\x01\x02'
- zb.send('tx_explicit',
- dest_addr_long = switchLongAddr,
- dest_addr = switchShortAddr,
- src_endpoint = '\x00',
- dest_endpoint = '\x00',
- cluster = '\x80\x06',
- profile = '\x00\x00',
- data = payload2
- )
- print 'Sent Match Descriptor'
- # Now there are two messages directed at the hardware
- # code (rather than the network code. The switch has to
- # receive both of these to stay joined.
- payload3 = '\x11\x01\x01'
- zb.send('tx_explicit',
- dest_addr_long = switchLongAddr,
- dest_addr = switchShortAddr,
- src_endpoint = '\x00',
- dest_endpoint = '\x02',
- cluster = '\x00\xf6',
- profile = '\xc2\x16',
- data = payload2
- )
- payload4 = '\x19\x01\xfa\x00\x01'
- zb.send('tx_explicit',
- dest_addr_long = switchLongAddr,
- dest_addr = switchShortAddr,
- src_endpoint = '\x00',
- dest_endpoint = '\x02',
- cluster = '\x00\xf0',
- profile = '\xc2\x16',
- data = payload4
- )
- print 'Sent hardware join messages'
- # now that it should have joined, I'll add a record to the database to
- # hold the status. I'll just name the device 'unknown' so it can
- # be updated by hand using sqlite3 directly. If the device already exists,
- # I'll leave the name alone and just use the existing record
- # Yes, this means you'll have to go into the database and assign it a name
- #
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- # See if the device is already in the database
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- switchrecord = c.fetchone()
- if switchrecord is not None:
- print "Device %s is rejoining the network" %(switchrecord[0])
- else:
- print "Adding new device"
- c.execute("insert into smartswitch(name, longaddress, shortaddress, type, status, watts, twatts, utime) values (?, ?, ?, ?, ?, ?, ?);", ('unknown', addrToString(data['source_addr_long']), addrToString(data['source_addr']), 'unknown', 'unknown', '0', '0', time.strftime("%A, %B, %d at %H:%M:%S")))
- dbconn.commit()
- dbconn.close()
- elif (clusterId == 0xef):
- clusterCmd = ord(data['rf_data'][2])
- # print "ClusterCmd: ", clusterCmd
- if (clusterCmd == 0x81):
- usage = ord(data['rf_data'][3]) + (ord(data['rf_data'][4]) * 256)
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- # This is commented out because I don't need the name
- # unless I'm debugging.
- # get device name from database
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- name = c.fetchone()[0]
- print "%s Instaneous Power: %d Watts" %(name, usage)
- # do database updates
- c.execute("update smartswitch set watts = ?, shortaddress = ?, utime = ? where longaddress = ?; ", (usage, addrToString(data['source_addr']), time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
- dbconn.commit()
- dbconn.close()
- elif (clusterCmd == 0x82):
- usage = (ord(data['rf_data'][3]) + (ord(data['rf_data'][4]) * 256) + (ord(data['rf_data'][5]) * 256 * 256) + (ord(data['rf_data'][6]) * 256 * 256 * 256) )
- upTime = (ord(data['rf_data'][7]) + (ord(data['rf_data'][8]) * 256) + (ord(data['rf_data'][9]) * 256 * 256) + (ord(data['rf_data'][10]) * 256 * 256 * 256) )
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- name = c.fetchone()[0]
- print "%s Minute Stats: Usage, %d Watt Hours; Uptime, %d Seconds" %(name, usage/3600, upTime)
- # update database stuff
- c.execute("update smartswitch set twatts = ?, shortaddress = ?, utime = ? where longaddress = ?; ", (usage, addrToString(data['source_addr']), time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
- dbconn.commit()
- dbconn.close()
- elif (clusterId == 0xf0):
- clusterCmd = ord(data['rf_data'][2])
- # print "Cluster Cmd:", hex(clusterCmd),
- if (clusterCmd == 0xfb):
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- name = c.fetchone()[0].encode("utf-8")
- tempdata = data['rf_data']
- ms = long(convtohex(tempdata[7])+convtohex(tempdata[6])+convtohex(tempdata[5])+convtohex(tempdata[4]), 16)
- print "%s Milliseconds: %s" %(name, ms)
- millidegree1 = str(int(convtohex(tempdata[9]), 16))
- millidegree2 = str(int(convtohex(tempdata[8]), 16))
- millidegree = millidegree1+millidegree2
- degree = (9.0/5.0 * (float(millidegree) / 1000) + 32)
- print "%s Temperature: %sºF" %(name, degree)
- devtype = ord(tempdata[3])
- if (devtype == 0x1c):
- print "%s Device Type: Smart Power Switch" %(name)
- if (devtype == 0x1f):
- print "%s Device Type: Door/Window Contact Sensor" %(name)
- # else:
- #print "Unimplemented"
- elif (clusterId == 0xf6):
- clusterCmd = ord(data['rf_data'][2])
- if (clusterCmd == 0xfd):
- print "RSSI value:", ord(data['rf_data'][3])
- elif (clusterCmd == 0xfe):
- print "Version Information:\n"
- versiondata = data['rf_data']
- versiondate = ''
- vendorstring = ''
- vendorstring += convtohex(versiondata[22])
- vendorstring += convtohex(versiondata[23])
- vendorstring += convtohex(versiondata[24])
- vendorstring += convtohex(versiondata[25])
- vendorstring += convtohex(versiondata[26])
- vendorstring += convtohex(versiondata[27])
- vendorstring += convtohex(versiondata[28])
- vendorstring += convtohex(versiondata[29])
- vendorstring += convtohex(versiondata[30])
- vendorstring += convtohex(versiondata[31])
- vendorstring += convtohex(versiondata[32])
- print "Vendor :", vendorstring.lstrip().decode('hex')
- devicetype = ''
- versloc = 34
- while True:
- if (convtohex(versiondata[versloc]) == "0a"):
- break
- else:
- devicetype += convtohex(versiondata[versloc])
- versloc += 1
- print "Device Type :", devicetype.lstrip().decode('hex')
- versloc += 1
- versmax = versloc + 10
- while versloc < versmax:
- versiondate += convtohex(versiondata[versloc])
- versloc += 1
- print "Version Date :", versiondate.lstrip().decode('hex')
- # else:
- # print data['rf_data']
- elif (clusterId == 0xee):
- clusterCmd = ord(data['rf_data'][2])
- status = ''
- if (clusterCmd == 0x80):
- if (ord(data['rf_data'][3]) & 0x01):
- status = "ON"
- else:
- status = "OFF"
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- name = c.fetchone()[0]
- print name
- print "Switch is", status
- c.execute("update smartswitch set status = ?, shortaddress = ?, utime = ? where longaddress = ?; ", (status, addrToString(data['source_addr']), time.strftime("%A, %B, %d at %H:%M:%S"), addrToString(data['source_addr_long'])))
- dbconn.commit()
- dbconn.close()
- elif (clusterId == 0x500):
- dbconn = sqlite3.connect(DATABASE)
- c = dbconn.cursor()
- c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
- name = c.fetchone()[0]
- switchmisc = ord(data['rf_data'][1])
- if (switchmisc == 0x6e):
- print "Contact Switch Status Change"
- switchstatus = ord(data['rf_data'][3])
- if (switchstatus == 0x00):
- print "%s Contact is Closed and Tamper Switch is Open" % name
- elif (switchstatus == 0x01):
- print "%s Contact is Open and Tamper Switch is Open" % name
- elif (switchstatus == 0x04):
- print "%s Contact is Closed and Tamper Switch is Closed" % name
- elif (switchstatus == 0x05):
- print "%s Contact is Open and Tamper Switch is Closed" % name
- else:
- print "%s Unknown Value: %d" %(name, switchstatus)
- elif (ord(data['rf_data'][3]) == 0x15 and ord(data['rf_data'][4]) == 0x00 and ord(data['rf_data'][5]) == 0x39 and ord(data['rf_data'][6]) == 0x10):
- payload = '\x11\x80\x00\x00\x05'
- zb.send('tx_explicit',
- dest_addr_long = switchLongAddr,
- dest_addr = switchShortAddr,
- src_endpoint = '\x00',
- dest_endpoint = '\x00',
- cluster = '\x05\x00',
- profile = '\xc2\x16',
- data = payload
- )
- else:
- print "%s Cluster ID 500 Unknown Value: %d" %(name, switchmisc)
- elif (clusterId == 0xf2):
- tamper = ord(data['rf_data'][1])
- print "Tamper Tripped"
- if (tamper == 0x01):
- print "Sensor Cover Closed"
- elif (tamper == 0x02):
- print "Sensor Cover Opened"
- # else:
- # print "Sensor Cover Status Unknown"
- else:
- print "Unimplemented Cluster ID", hex(clusterId)
- print
- def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint,
- clusterId, profileId, clusterCmd, databytes):
- payload = '\x11\x00' + clusterCmd + databytes
- # print 'payload',
- # for c in payload:
- # print hex(ord(c)),
- # print
- # print 'long address:',
- # for c in whereLong:
- # print hex(ord(c)),
- # print
- zb.send('tx_explicit',
- dest_addr_long = whereLong,
- dest_addr = whereShort,
- src_endpoint = srcEndpoint,
- dest_endpoint = destEndpoint,
- cluster = clusterId,
- profile = profileId,
- data = payload
- )
- # This just puts a time stamp in the log file for tracking
- def timeInLog():
- print time.strftime("%A, %B, %d at %H:%M:%S")
- #-----------------------------------------------------------------
- # Create XBee library API object, which spawns a new thread
- zb = ZigBee(ser, callback=messageReceived)
- print "started at ", time.strftime("%A, %B, %d at %H:%M:%S")
- while True:
- try:
- time.sleep(0.1)
- sys.stdout.flush() # if you're running non interactive, do this
- except KeyboardInterrupt:
- print "Keyboard interrupt"
- break
- except NameError as e:
- print "NameError:",
- print e.message.split("'")[1]
- break
- except:
- print "Unexpected error:", sys.exc_info()[0]
- break
- print "After the while loop"
- # halt() must be called before closing the serial
- # port in order to ensure proper thread shutdown
- zb.halt()
- ser.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement