Advertisement
Guest User

XBeeIrisMonitor.py

a guest
Aug 14th, 2014
490
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.93 KB | None | 0 0
  1. #! /usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3.  
  4. from xbee import ZigBee
  5. #from apscheduler.scheduler import Scheduler
  6. import logging
  7. import datetime
  8. import time
  9. import serial
  10. import sys
  11. import shlex
  12. import sqlite3
  13. import struct
  14. from string import *
  15.  
  16. #-------------------------------------------------
  17. # the database where I'm storing stuff
  18. DATABASE='/root/XBeeIris/IrisSwitches'
  19.  
  20. # on the Raspberry Pi the serial port is ttyAMA0
  21. XBEEPORT = '/dev/ttyAMA0'
  22. #XBEEPORT = '/dev/ttyUSB0'
  23. XBEEBAUD_RATE = 9600
  24.  
  25. # The XBee addresses I'm dealing with
  26. BROADCAST = '\x00\x00\x00\x00\x00\x00\xff\xff'
  27. UNKNOWN = '\xff\xfe' # This is the 'I don't know' 16 bit address
  28.  
  29. switchLongAddr = '12'
  30. switchShortAddr = '12'
  31.  
  32. #-------------------------------------------------
  33. logging.basicConfig()
  34.  
  35. # this is the only way I could think of to get the address strings to store.
  36. # I take the ord() to get a number, convert to hex, then take the 3 to end
  37. # characters and pad them with zero and finally put the '0x' back on the front
  38. # I put spaces in between each hex character to make it easier to read.  This
  39. # left an extra space at the end, so I slice it off in the return statement.
  40. # I hope this makes it easier to grab it out of the database when needed
  41. def addrToString(funnyAddrString):
  42.  hexified = ''
  43.  for i in funnyAddrString:
  44.   hexified += '0x' + hex(ord(i))[2:].zfill(2) + ' '
  45.  return hexified[:-1]
  46.  
  47. def convtohex(binarydata):
  48.  hexdata = ''
  49.  for i in binarydata:
  50.   hexdata += hex(ord(i))[2:].zfill(2) + ' '
  51.  return hexdata[:-1]
  52.  
  53. #------------ XBee Stuff -------------------------
  54. # Open serial port for use by the XBee
  55. ser = serial.Serial(XBEEPORT, XBEEBAUD_RATE)
  56.  
  57. # this is a call back function.  When a message
  58. # comes in this function will get the data
  59. def messageReceived(data):
  60.     print 'got packet: %s' %(data)
  61.     print "Hex: %s" %(convtohex(data['rf_data']))
  62.     # This is a test program, so use global variables and
  63.     # save the addresses so they can be used later
  64.     global switchLongAddr
  65.     global switchShortAddr
  66.     switchLongAddr = data['source_addr_long']
  67.     switchShortAddr = data['source_addr']
  68.     clusterId = (ord(data['cluster'][0])*256) + ord(data['cluster'][1])
  69.     print 'Cluster ID:', hex(clusterId),
  70.     if (clusterId == 0x13):
  71.         # This is the device announce message.
  72.         # due to timing problems with the switch itself, I don't
  73.         # respond to this message, I save the response for later after the
  74.         # Match Descriptor request comes in.  You'll see it down below.
  75.         # if you want to see the data that came in with this message, just
  76.         # uncomment the 'print data' comment up above
  77.         print 'Device Announce Message'
  78.     elif (clusterId == 0x8005):
  79.         # this is the Active Endpoint Response This message tells you
  80.         # what the device can do, but it isn't constructed correctly to match
  81.         # what the switch can do according to the spec.  This is another
  82.         # message that gets it's response after I receive the Match Descriptor
  83.         print 'Active Endpoint Response'
  84.     elif (clusterId == 0x0006):
  85.         # Match Descriptor Request; this is the point where I finally
  86.         # respond to the switch.  Several messages are sent to cause the
  87.         # switch to join with the controller at a network level and to cause
  88.         # it to regard this controller as valid.
  89.         #
  90.         # First the Active Endpoint Request
  91.         payload1 = '\x00\x00'
  92.         zb.send('tx_explicit',
  93.             dest_addr_long = switchLongAddr,
  94.             dest_addr = switchShortAddr,
  95.             src_endpoint = '\x00',
  96.             dest_endpoint = '\x00',
  97.             cluster = '\x00\x05',
  98.             profile = '\x00\x00',
  99.             data = payload1
  100.         )
  101.         print 'sent Active Endpoint'
  102.         # Now the Match Descriptor Response
  103.         payload2 = '\x00\x00\x00\x00\x01\x02'
  104.         zb.send('tx_explicit',
  105.             dest_addr_long = switchLongAddr,
  106.             dest_addr = switchShortAddr,
  107.             src_endpoint = '\x00',
  108.             dest_endpoint = '\x00',
  109.             cluster = '\x80\x06',
  110.             profile = '\x00\x00',
  111.             data = payload2
  112.         )
  113.         print 'Sent Match Descriptor'
  114.         # Now there are two messages directed at the hardware
  115.         # code (rather than the network code.  The switch has to
  116.         # receive both of these to stay joined.
  117.         payload3 = '\x11\x01\x01'
  118.         zb.send('tx_explicit',
  119.             dest_addr_long = switchLongAddr,
  120.             dest_addr = switchShortAddr,
  121.             src_endpoint = '\x00',
  122.             dest_endpoint = '\x02',
  123.             cluster = '\x00\xf6',
  124.             profile = '\xc2\x16',
  125.             data = payload2
  126.         )
  127.         payload4 = '\x19\x01\xfa\x00\x01'
  128.         zb.send('tx_explicit',
  129.             dest_addr_long = switchLongAddr,
  130.             dest_addr = switchShortAddr,
  131.             src_endpoint = '\x00',
  132.             dest_endpoint = '\x02',
  133.             cluster = '\x00\xf0',
  134.             profile = '\xc2\x16',
  135.             data = payload4
  136.         )
  137.         print 'Sent hardware join messages'
  138.  
  139.         # now that it should have joined, I'll add a record to the database to
  140.         # hold the status.  I'll just name the device 'unknown' so it can
  141.         # be updated by hand using sqlite3 directly.  If the device already exists,
  142.         # I'll leave the name alone and just use the existing record
  143.         # Yes, this means you'll have to go into the database and assign it a name
  144.         #
  145.         dbconn = sqlite3.connect(DATABASE)
  146.         c = dbconn.cursor()
  147.         # See if the device is already in the database
  148.         c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  149.         switchrecord = c.fetchone()
  150.         if switchrecord is not None:
  151.          print "Device %s is rejoining the network" %(switchrecord[0])
  152.         else:
  153.          print "Adding new device"
  154.          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")))
  155.          dbconn.commit()
  156.          dbconn.close()
  157.     elif (clusterId == 0xef):
  158.         clusterCmd = ord(data['rf_data'][2])
  159. #        print "ClusterCmd: ", clusterCmd
  160.         if (clusterCmd == 0x81):
  161.          usage = ord(data['rf_data'][3]) + (ord(data['rf_data'][4]) * 256)
  162.          dbconn = sqlite3.connect(DATABASE)
  163.          c = dbconn.cursor()
  164.          # This is commented out because I don't need the name
  165.          # unless I'm debugging.
  166.          # get device name from database
  167.          c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  168.          name = c.fetchone()[0]
  169.          print "%s Instaneous Power: %d Watts" %(name, usage)
  170.          # do database updates
  171.          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'])))
  172.          dbconn.commit()
  173.          dbconn.close()
  174.         elif (clusterCmd == 0x82):
  175.          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) )
  176.          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) )
  177.          dbconn = sqlite3.connect(DATABASE)
  178.          c = dbconn.cursor()
  179.          c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  180.          name = c.fetchone()[0]
  181.          print "%s Minute Stats: Usage, %d Watt Hours; Uptime, %d Seconds" %(name, usage/3600, upTime)
  182.          # update database stuff
  183.          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'])))
  184.          dbconn.commit()
  185.          dbconn.close()
  186.     elif (clusterId == 0xf0):
  187.      clusterCmd = ord(data['rf_data'][2])
  188.      # print "Cluster Cmd:", hex(clusterCmd),
  189.      if (clusterCmd == 0xfb):
  190.        dbconn = sqlite3.connect(DATABASE)
  191.        c = dbconn.cursor()
  192.        c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  193.        name = c.fetchone()[0].encode("utf-8")
  194.        tempdata = data['rf_data']
  195.        ms = long(convtohex(tempdata[7])+convtohex(tempdata[6])+convtohex(tempdata[5])+convtohex(tempdata[4]), 16)
  196.        print "%s Milliseconds: %s" %(name, ms)
  197.        millidegree1 = str(int(convtohex(tempdata[9]), 16))
  198.        millidegree2 = str(int(convtohex(tempdata[8]), 16))
  199.        millidegree = millidegree1+millidegree2
  200.        degree = (9.0/5.0 * (float(millidegree) / 1000) + 32)
  201.        print "%s Temperature: %sºF" %(name, degree)
  202.        devtype = ord(tempdata[3])
  203.        if (devtype == 0x1c):
  204.          print "%s Device Type: Smart Power Switch" %(name)
  205.        if (devtype == 0x1f):
  206.          print "%s Device Type: Door/Window Contact Sensor" %(name)
  207.      # else:
  208.       #print "Unimplemented"
  209.     elif (clusterId == 0xf6):
  210.      clusterCmd = ord(data['rf_data'][2])
  211.      if (clusterCmd == 0xfd):
  212.        print "RSSI value:", ord(data['rf_data'][3])
  213.      elif (clusterCmd == 0xfe):
  214.        print "Version Information:\n"
  215.        versiondata = data['rf_data']
  216.        versiondate = ''
  217.        vendorstring = ''
  218.        vendorstring += convtohex(versiondata[22])
  219.        vendorstring += convtohex(versiondata[23])
  220.        vendorstring += convtohex(versiondata[24])
  221.        vendorstring += convtohex(versiondata[25])
  222.        vendorstring += convtohex(versiondata[26])
  223.        vendorstring += convtohex(versiondata[27])
  224.        vendorstring += convtohex(versiondata[28])
  225.        vendorstring += convtohex(versiondata[29])
  226.        vendorstring += convtohex(versiondata[30])
  227.        vendorstring += convtohex(versiondata[31])
  228.        vendorstring += convtohex(versiondata[32])
  229.        print "Vendor       :", vendorstring.lstrip().decode('hex')
  230.  
  231.        devicetype = ''
  232.        versloc = 34
  233.        while True:
  234.          if (convtohex(versiondata[versloc]) == "0a"):
  235.            break
  236.          else:
  237.            devicetype += convtohex(versiondata[versloc])
  238.          versloc += 1
  239.  
  240.        print "Device Type  :", devicetype.lstrip().decode('hex')
  241.  
  242.        versloc += 1
  243.        versmax = versloc + 10
  244.        while versloc < versmax:
  245.          versiondate += convtohex(versiondata[versloc])
  246.          versloc += 1
  247.  
  248.        print "Version Date :", versiondate.lstrip().decode('hex')
  249.  
  250. #     else:
  251. #       print data['rf_data']
  252.     elif (clusterId == 0xee):
  253.      clusterCmd = ord(data['rf_data'][2])
  254.      status = ''
  255.      if (clusterCmd == 0x80):
  256.       if (ord(data['rf_data'][3]) & 0x01):
  257.        status = "ON"
  258.       else:
  259.        status = "OFF"
  260.       dbconn = sqlite3.connect(DATABASE)
  261.       c = dbconn.cursor()
  262.       c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  263.       name = c.fetchone()[0]
  264.       print name
  265.       print "Switch is", status
  266.       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'])))
  267.       dbconn.commit()
  268.       dbconn.close()
  269.     elif (clusterId == 0x500):
  270.       dbconn = sqlite3.connect(DATABASE)
  271.       c = dbconn.cursor()
  272.       c.execute("select name from smartswitch where longaddress = ?; ", (addrToString(data['source_addr_long']),))
  273.       name = c.fetchone()[0]
  274.       switchmisc = ord(data['rf_data'][1])
  275.       if (switchmisc == 0x6e):
  276.         print "Contact Switch Status Change"
  277.         switchstatus = ord(data['rf_data'][3])
  278.         if (switchstatus == 0x00):
  279.           print "%s Contact is Closed and Tamper Switch is Open" % name
  280.         elif (switchstatus == 0x01):
  281.           print "%s Contact is Open and Tamper Switch is Open" % name
  282.         elif (switchstatus == 0x04):
  283.           print "%s Contact is Closed and Tamper Switch is Closed" % name
  284.         elif (switchstatus == 0x05):
  285.           print "%s Contact is Open and Tamper Switch is Closed" % name
  286.         else:
  287.           print "%s Unknown Value: %d" %(name, switchstatus)
  288.       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):
  289.           payload = '\x11\x80\x00\x00\x05'
  290.           zb.send('tx_explicit',
  291.               dest_addr_long = switchLongAddr,
  292.               dest_addr = switchShortAddr,
  293.               src_endpoint = '\x00',
  294.               dest_endpoint = '\x00',
  295.               cluster = '\x05\x00',
  296.               profile = '\xc2\x16',
  297.               data = payload
  298.           )
  299.       else:
  300.           print "%s Cluster ID 500 Unknown Value: %d" %(name, switchmisc)
  301.  
  302.     elif (clusterId == 0xf2):
  303.       tamper = ord(data['rf_data'][1])
  304.       print "Tamper Tripped"
  305.       if (tamper == 0x01):
  306.         print "Sensor Cover Closed"
  307.       elif (tamper == 0x02):
  308.         print "Sensor Cover Opened"
  309. #      else:
  310. #        print "Sensor Cover Status Unknown"
  311.     else:
  312.      print "Unimplemented Cluster ID", hex(clusterId)
  313.      print
  314.  
  315. def sendSwitch(whereLong, whereShort, srcEndpoint, destEndpoint,
  316.     clusterId, profileId, clusterCmd, databytes):
  317.    
  318.     payload = '\x11\x00' + clusterCmd + databytes
  319.     # print 'payload',
  320.     # for c in payload:
  321.         # print hex(ord(c)),
  322.     # print
  323.     # print 'long address:',
  324.     # for c in whereLong:
  325.         # print hex(ord(c)),
  326.     # print
  327.        
  328.     zb.send('tx_explicit',
  329.         dest_addr_long = whereLong,
  330.         dest_addr = whereShort,
  331.         src_endpoint = srcEndpoint,
  332.         dest_endpoint = destEndpoint,
  333.         cluster = clusterId,
  334.         profile = profileId,
  335.         data = payload
  336.         )
  337.  
  338. # This just puts a time stamp in the log file for tracking
  339. def timeInLog():
  340.  print time.strftime("%A, %B, %d at %H:%M:%S")
  341.    
  342. #-----------------------------------------------------------------
  343.  
  344. # Create XBee library API object, which spawns a new thread
  345. zb = ZigBee(ser, callback=messageReceived)
  346.  
  347. print "started at ", time.strftime("%A, %B, %d at %H:%M:%S")
  348. while True:
  349.  try:
  350.   time.sleep(0.1)
  351.   sys.stdout.flush() # if you're running non interactive, do this
  352.  
  353.  except KeyboardInterrupt:
  354.    print "Keyboard interrupt"
  355.    break
  356.  except NameError as e:
  357.    print "NameError:",
  358.    print e.message.split("'")[1]
  359.    break
  360.  except:
  361.    print "Unexpected error:", sys.exc_info()[0]
  362.    break
  363.  
  364. print "After the while loop"
  365. # halt() must be called before closing the serial
  366. # port in order to ensure proper thread shutdown
  367. zb.halt()
  368. ser.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement