diff --git a/README.md b/README.md index a7379d8..b168117 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,14 @@ Python Class for utilizing the Rainforest Automation Eagle ( RFA-Z109 ) socket API - *** This is just skeleton so far *** + import import RainEagle + + raineagle = RainEagle.Eagle( debug=0 , addr="10.1.1.39") + ret_data = eg.list_devices() + + print "device MacID = ", ret_data['DeviceInfo']['DeviceMacId'] + + Rainforest Automation Documentation diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 639a2a2..34f0704 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -5,10 +5,15 @@ import os import time import xml.etree.ElementTree as ET +from pprint import pprint + + + __all__ = ['Eagle'] def et2d(et) : + """ Etree to Dict converts an ETree to a Dict Tree @@ -55,7 +60,17 @@ def et2d(et) : # # Simple Base class for ISY Class class Eagle(object) : + """ + Class for talking to Rainforest Automation EAGLE (RFA-Z109) + args: + debug print debug messages if true + addr address of device + port port on device (default 5002) + getmac connect to device at start up and get macid (default true) + + Currently there is very little error handling ( if any at all ) + """ def __init__(self, **kwargs): self.debug = kwargs.get("debug", 0) @@ -63,143 +78,71 @@ class Eagle(object) : print self.__class__.__name__, __name__ self.addr = kwargs.get("addr", os.getenv('EAGLE_ADDR', None)) self.port = kwargs.get("port", os.getenv('EAGLE_PORT', 5002)) + self.getmac = kwargs.get("getmac", True) self.soc = None + self.macid = None + # preload + if self.getmac : + self.device_info = self.list_devices() + if self.debug : + pprint(self.device_info) + # self.macid = self.device_info['DeviceInfo']['DeviceMacId'] + if self.debug : + print "DeviceMacId = ", self.macid - def connect(self) : - self.soc = socket.create_connection( (self.addr, self.port), 10) - -# self.soc_rf = self.soc.makefile("rb") -# self.soc_wf = self.soc.makefile("wb") - - def disconnect(self): - -# try : -# if self.soc_rf : -# self.soc_rf.close() -# self.soc_rf = False -# except IOError : -# pass -# -# try : -# if self.soc_wf : -# self.soc_wf.close() -# self.soc_wf = False -# except IOError : -# pass -# - try : - if self.soc : - self.soc_sock.close() - self.soc_sock = False - except IOError : - pass - - def _reconnect(self): - self.disconnect() - self.connect() - - - def _send_comm(self, cmd, **kwargs): - commstr = "\r\n " - commstr += "{0!s}\r\n".format(cmd) - for k, v in kwargs.items() : - commstr += "<{0}>{1!s}\r\n".format(k, v) - commstr += "\r\n" - -#+ self.soc.send(commstr) - print "commstr : ", commstr - -# self.soc_wf.write(commstr) -# self.soc_wf.flush() - -#+ time.sleep(1) - - replystr = "" -#+ while 1 : -#+ buf = self.soc.recv(1000) -#+ if not buf: -#+ break -#+ replystr += buf - - return replystr - # commands as class funtions def list_devices(self): comm_responce = self._send_comm("list_devices") - # temp debug data - comm_responce = "" \ - " 0xd8d5b90000000xxx" \ - " 0x9ac4382dffa81xxx" \ - " 7e572b66c5b444xxx" \ - " 94227dca4e773xxx" \ - " 1.4.27 (5278)" \ - " 1.2.3" \ - " Rainforest Automation, I" \ - " RFA-Z109 EAGLE" \ - " 20130308PO020621" \ - "\n" - + if self.debug : + print "comm_responce =", comm_responce etree = ET.fromstring('' + comm_responce + '' ) rv = et2d(etree) + if self.macid == None : + self.macid = rv['DeviceInfo']['DeviceMacId'] return rv # 3 - def get_device_data(self, macid) : + def get_device_data(self, macid=None) : """ Send the GET_DEVICE_DATA command to get a data dump """ + if macid == None : + macid = self.macid comm_responce = self._send_comm("get_device_data", MacId=macid) - # temp debug data - comm_responce = "" \ - " 0xd8d5b90000000xxx" \ - " Rejoining" \ - " 0x001350030011bxxx" \ - " 0x7fffffffffffffff" \ - " 0x0000ffff" \ - " 24" \ - " 156" \ - "" \ - "" \ - " 0xd8d5b90000000xxx" \ - " 0x9ac4382dffa81xxx" \ - " 7e572b66c5b44xxx" \ - " 94227dca4e773xxx" \ - " 1.4.27 (5278)" \ - " 1.2.3" \ - " Rainforest Automation, I" \ - " RFA-Z109 EAGLE" \ - " 20130308PO020621" \ - "" \ - "" \ - " 0xd8d5b90000000xxx" \ - " 0x001350030011bxxx" \ - " 0x00000bf1" \ - " 0x195193e3" \ - " 0x00000001" \ - " 0x000003e8" \ - " 0x00000003" \ - " 0x0000000f" \ - " 0x0001" \ - "" - etree = ET.fromstring('' + comm_responce + '' ) rv = et2d(etree) return rv # 10 - def get_instantaneous_demand(self, macid, interval) : - """ Send the GET_INSTANTANEOUS_DEMAND command to get the real time demand from the meter""" + def get_instantaneous_demand(self, macid=None) : + """ Send the GET_INSTANTANEOUS_DEMAND command + get the real time demand from the meter + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + """ + if macid == None : + macid = self.macid comm_responce = self._send_comm("get_instantaneous_demand", - MacId=macid, Interval=interval) + MacId=macid) etree = ET.fromstring('' + comm_responce + '' ) rv = et2d(etree) return rv # 11 - def get_demand_values(self, macid, interval, frequency=None ) : - """ Send the GET_DEMAND_VALUES command to get a series of instantaneous demand values""" + def get_demand_values(self, macid=None, interval="hour", frequency=None ) : + """ Send the GET_DEMAND_VALUES command + get a series of instantaneous demand values + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + Interval hour | day | week + [Frequency] int seconds between samples + """ + if macid == None : + macid = self.macid kwargs = {"MacId": macid, "Interval": interval} if frequency : kwargs["Frequency"] = frequency @@ -209,8 +152,16 @@ class Eagle(object) : return rv # 12 - def get_summation_values(self, macid, interval) : - """ Send the GET_SUMMATION_VALUES command to get a series of net summation values """ + def get_summation_values(self, macid=None, interval="day") : + """ Send the GET_SUMMATION_VALUES command + get a series of net summation values + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + Interval day | week | month | year + """ + if macid == None : + macid = self.macid comm_responce = self._send_comm("get_summation_values", MacId=macid, Interval=interval ) etree = ET.fromstring('' + comm_responce + '' ) @@ -218,8 +169,22 @@ class Eagle(object) : return rv # 14 - def set_fast_poll(self, macid, frequency, duration) : - """ set the fast poll mode on the meter. """ + def set_fast_poll(self, macid=None, frequency="0x04", duration="0xFF") : + """ Send the SET_FAST_POLL command + set the fast poll mode on the meter + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + Frequency 0x01 - 0xFF Freq to poll meter, in seconds + Duration 0x00 - 0x0F Duration of fast poll mode, in minutes (max 15) + """ + if macid == None : + macid = self.macid + if isinstance(frequency, int) : + frequency = "{:#04x}".format(m) + if isinstance(duration, int) : + frequency = "{:#04x}".format(m) + comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid, Frequency=frequency, Duration=duration) etree = ET.fromstring('' + comm_responce + '' ) @@ -227,8 +192,15 @@ class Eagle(object) : return rv # 15 - def get_fast_poll_status(self, macid) : - """ get the current status of fast poll mode. """ + def get_fast_poll_status(self, macid=None) : + """ Send the GET_FAST_POLL_STATUS command + get the current status of fast poll mode. + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + """ + if macid == None : + macid = self.macid comm_responce = self._send_comm("get_fast_poll_status", MacId=macid) etree = ET.fromstring('' + comm_responce + '' ) rv = et2d(etree) @@ -236,8 +208,12 @@ class Eagle(object) : # 17 - def get_history_data(self, macid, starttime, endtime=None, frequency=None ) : - """ get a series of summation values over an interval of time """ + def get_history_data(self, macid=None, starttime="0x00000000", endtime=None, frequency=None ) : + """ Send the GET_HISTORY_DATA command + get a series of summation values over an interval of time + """ + if macid == None : + macid = self.macid kwargs = {"MacId": macid, "StartTime": starttime} if endtime : kwargs["EndTime"] = endtime @@ -248,6 +224,61 @@ class Eagle(object) : rv = et2d(etree) return rv + + # Support functions + + def connect(self) : + self.soc = socket.create_connection( (self.addr, self.port), 10) + + def disconnect(self): + try : + if self.soc : + self.soc.close() + self.soc = False + except IOError : + pass + + + def _send_comm(self, cmd, **kwargs): + + if cmd == "set_fast_poll" : + command_tag = "RavenCommand" + else : + command_tag = "LocalCommand" + + commstr = "<{0}>\n ".format(command_tag) + commstr += "{0!s}\n".format(cmd) + for k, v in kwargs.items() : + commstr += "<{0}>{1!s}\n".format(k, v) + commstr += "\n".format(command_tag) + + self.connect() + self.soc.sendall(commstr) + if self.debug : + print "commstr : \n", commstr + + # time.sleep(1) + + replystr = "" + while 1 : + buf = self.soc.recv(1000) + if not buf: + break + replystr += buf + + self.disconnect() + return replystr + + def to_unix_time(self, t) : + """ converts time stored as + offset in seconds from "Jan 1 00:00:00 2000" + to unix's epoch of 1970 + """ + if isinstance(t, (int, long, float) ) : + return t + 946684800 + if isinstance(t, str) and t.startswith('0x') : + return 946684800 + int(t, 16) + # Do nothing # (syntax check) # diff --git a/RainEagle/__init__.py b/RainEagle/__init__.py index a2a1671..34337b7 100644 --- a/RainEagle/__init__.py +++ b/RainEagle/__init__.py @@ -2,21 +2,29 @@ """ + import sys if sys.hexversion < 0x20703f0 : sys.stderr.write("You need python 2.7 or later to run this script\n") +__revision__ = "$Id: 20140301 $" +__version__ = '0.1.20140301' +__author__ = 'Peter Shipley ' +__copyright__ = "Copyright (C) 2014 Peter Shipley" +__license__ = "BSD" + -from RainEagle.EagleClass import Eagle +from EagleClass import Eagle +#from RainEagle.EagleClass import Eagle __all__ = ['Eagle'] if __name__ == "__main__": - import __main__ + #import __main__ #print(__main__.__file___) - print("ISY.__init__") + print("RainEagle.__init__") print("syntax ok") exit(0) diff --git a/test.py b/test.py index fa8b870..e30329c 100644 --- a/test.py +++ b/test.py @@ -1,12 +1,56 @@ import RainEagle +import time from pprint import pprint +def too_unix_time(t) : + """ converts time stored as + offset in seconds from "Jan 1 00:00:00 2000" + to unix's epoch of 1970 + """ + if isinstance(t, (int, long, float) ) : + return t + 946684800 + if isinstance(t, str) and t.startswith('0x') : + return 946684800 + int(t, 16) -eg = RainEagle.Eagle( debug=1 ) +eg = RainEagle.Eagle( debug=0 , addr="10.1.1.39") + + +print "\nlist_devices :" r = eg.list_devices() pprint(r) -r = eg.get_device_data("EE:24:01:05:50:23") +# print "\nget_device_data :" +# r = eg.get_device_data() +# pprint(r) +# time_stamp_str=r['InstantaneousDemand']['TimeStamp'] +# time_stamp = eg.to_unix_time(time_stamp_str) +# print "time = ", time.asctime(time.localtime(time_stamp)) + + +print "\nget_instantaneous_demand :" +r = eg.get_instantaneous_demand() +pprint(r) + + +print "\nget_demand_values :" +r = eg.get_demand_values(eg.macid, interval="hour") pprint(r) + +print "\nget_summation_values :" +r = eg.get_summation_values(eg.macid, interval="day") +pprint(r) + +# set_fast_poll(self, macid=None, frequency, duration) : + + +print "\nget_fast_poll_status :" +r = eg.get_fast_poll_status(eg.macid) +pprint(r) + +etime = eg.to_unix_time( r['FastPollStatus']['EndTime']) +print "EndTime = ", time.asctime(time.localtime(etime)) + +# def get_history_data(self, macid=None, starttime, +# endtime=None, frequency=None ) :