From 86c0364fb86f5f3019532644f32c1c328bad0200 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Mon, 3 Mar 2014 17:52:11 -0800 Subject: [PATCH 01/17] backup --- README.md | 5 ++-- RainEagle/EagleClass.py | 66 ++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8202c54..012ea99 100644 --- a/README.md +++ b/README.md @@ -21,15 +21,16 @@ API Call list : list_devices() get_demand_values(macid=None, interval='hour', frequency=None) get_device_data(macid=None) - get_fast_poll_status(macid=None) get_history_data(macid=None, starttime='0x00000000', endtime=None, frequency=None) get_instantaneous_demand(macid=None) get_summation_values(macid=None, interval='day') + get_fast_poll_status(macid=None) set_fast_poll(macid=None, frequency='0x04', duration='0xFF') ``` -Calls return dictionarys containing data results +API Calls return dictionarys containing data results +returns None if error ## External Documentation diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 9bb0543..5d94be4 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -17,7 +17,7 @@ from pprint import pprint __all__ = ['Eagle'] -def et2d(et) : +def _et2d(et) : """ Etree to Dict @@ -50,9 +50,9 @@ def et2d(et) : d[child.tag] = [t] if list(child) or child.attrib : if child.tag in d : - d[child.tag].append(et2d(child)) + d[child.tag].append(_et2d(child)) else : - d[child.tag] = et2d(child) + d[child.tag] = _et2d(child) else : if child.tag in d : d[child.tag].append(child.text) @@ -104,7 +104,7 @@ class Eagle(object) : if self.debug : print "comm_responce =", comm_responce etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) if self.macid == None : self.macid = rv['DeviceInfo']['DeviceMacId'] return rv @@ -116,7 +116,7 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_device_data", MacId=macid) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # 10 @@ -132,7 +132,7 @@ class Eagle(object) : comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # 11 @@ -152,7 +152,7 @@ class Eagle(object) : kwargs["Frequency"] = frequency comm_responce = self._send_comm("get_demand_values", **kwargs) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # 12 @@ -169,7 +169,7 @@ class Eagle(object) : comm_responce = self._send_comm("get_summation_values", MacId=macid, Interval=interval ) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # 14 @@ -192,7 +192,7 @@ class Eagle(object) : comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid, Frequency=frequency, Duration=duration) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # 15 @@ -207,7 +207,7 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_fast_poll_status", MacId=macid) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv @@ -223,18 +223,18 @@ class Eagle(object) : kwargs["EndTime"] = endtime if frequency : kwargs["Frequency"] = frequency - comm_responce = self._send_comm("get_fast_poll_status", **kwargs) + comm_responce = self._send_comm("get_history_data", **kwargs) etree = ET.fromstring('' + comm_responce + '' ) - rv = et2d(etree) + rv = _et2d(etree) return rv # Support functions - def connect(self) : + def _connect(self) : self.soc = socket.create_connection( (self.addr, self.port), 10) - def disconnect(self): + def _disconnect(self): try : if self.soc : self.soc.close() @@ -256,22 +256,28 @@ class Eagle(object) : 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 + try: + 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 + + except Exception: + print("Unexpected error:", sys.exc_info()[0]) + print(ddat) + replystr = None + finally: + self._disconnect() + return replystr def to_unix_time(self, t) : """ converts time stored as From 44121358fee5bd934e4b567415cb611d9833a1cc Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:33:28 -0800 Subject: [PATCH 02/17] may cause API to stop responding --- Tests/get_hist.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 Tests/get_hist.py diff --git a/Tests/get_hist.py b/Tests/get_hist.py new file mode 100755 index 0000000..30ce7ec --- /dev/null +++ b/Tests/get_hist.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +""" +based on TalkToEagle.py +""" + + +import socket +import sys +import time + +my_macid = "0xd8d5b90000001296" + + + +# Enter your Eagle's IP below +Eagle_IP = "10.1.1.39" + +## list_devices + +s = socket.create_connection( (Eagle_IP, 5002), 10) +print s +time.sleep(1) + +sendstr = "\nlist_devices\n\n" + +s.sendall(sendstr) +print +print "sending to Eagle: \n\r" +print sendstr +print + +time.sleep(1) + +print "Eagle response: \n\r" + +while 1: + buf = s.recv(1000) + if not buf: + break + sys.stdout.write(buf) + +s.close() + + +## get_history_data + +s = socket.create_connection( (Eagle_IP, 5002), 10) +print s + +time.sleep(1) + +sendstr = "\nget_history_data\n0xd8d5b90000001296\n0x00000000\n\n".format(my_macid) + +s.sendall(sendstr) +print +print "sending to Eagle: \n\r" +print sendstr +print + +time.sleep(1) + +print "Eagle response: \n\r" + +while 1: + buf = s.recv(1000) + if not buf: + break + sys.stdout.write(buf) + +s.close() + + +exit(0) From 44a86721993b960d24e6caab53fe5a757e3ee7fd Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:51:43 -0800 Subject: [PATCH 03/17] crash Eagle --- Tests/get_hist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/get_hist.py b/Tests/get_hist.py index 30ce7ec..f50a525 100755 --- a/Tests/get_hist.py +++ b/Tests/get_hist.py @@ -50,7 +50,7 @@ print s time.sleep(1) -sendstr = "\nget_history_data\n0xd8d5b90000001296\n0x00000000\n\n".format(my_macid) +sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) s.sendall(sendstr) print From d1f622c292954a9e1090d412d1963951d6551201 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:54:34 -0800 Subject: [PATCH 04/17] clean up --- Tests/get_hist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/get_hist.py b/Tests/get_hist.py index f50a525..c51ab96 100755 --- a/Tests/get_hist.py +++ b/Tests/get_hist.py @@ -50,7 +50,7 @@ print s time.sleep(1) -sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) +sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) s.sendall(sendstr) print From 7ed00575c35d1adca013584b01ecce15afdd86c4 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:56:06 -0800 Subject: [PATCH 05/17] clean up --- Tests/get_hist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/get_hist.py b/Tests/get_hist.py index c51ab96..a664293 100755 --- a/Tests/get_hist.py +++ b/Tests/get_hist.py @@ -50,7 +50,7 @@ print s time.sleep(1) -sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) +sendstr = "\nget_history_data\n{0}\n0x00000000\n\n".format(my_macid) s.sendall(sendstr) print From 3ff208cccbe5278d2d1c96c54020b5c7bdeab1f2 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 18:01:41 -0800 Subject: [PATCH 06/17] debug and docs --- RainEagle/EagleClass.py | 43 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 5d94be4..4341de2 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -83,17 +83,26 @@ class Eagle(object) : 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.timeout = kwargs.get("timeout", 10) self.soc = None self.macid = None + if self.debug : + print "Addr : = ", self.addr + print "timeout : = ", self.timeout + print "debug : = ", self.debug + # preload if self.getmac : self.device_info = self.list_devices() + if self.device_info == None : + raise IOError("Error connecting") if self.debug : + print "__init__ ", pprint(self.device_info) # self.macid = self.device_info['DeviceInfo']['DeviceMacId'] if self.debug : - print "DeviceMacId = ", self.macid + print "Init DeviceMacId = ", self.macid @@ -103,6 +112,8 @@ class Eagle(object) : comm_responce = self._send_comm("list_devices") if self.debug : print "comm_responce =", comm_responce + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) if self.macid == None : @@ -115,6 +126,8 @@ class Eagle(object) : if macid == None : macid = self.macid comm_responce = self._send_comm("get_device_data", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -131,6 +144,8 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -151,6 +166,8 @@ class Eagle(object) : if frequency : kwargs["Frequency"] = frequency comm_responce = self._send_comm("get_demand_values", **kwargs) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -168,6 +185,8 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_summation_values", MacId=macid, Interval=interval ) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -191,6 +210,8 @@ class Eagle(object) : comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid, Frequency=frequency, Duration=duration) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -206,6 +227,8 @@ class Eagle(object) : if macid == None : macid = self.macid comm_responce = self._send_comm("get_fast_poll_status", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -215,6 +238,12 @@ class Eagle(object) : 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 + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + StartTime the start of the history interval (default oldest sample) + EndTime the end of the history interval (default current time) + Frequency Requested number of seconds between samples. """ if macid == None : macid = self.macid @@ -224,6 +253,8 @@ class Eagle(object) : if frequency : kwargs["Frequency"] = frequency comm_responce = self._send_comm("get_history_data", **kwargs) + if comm_responce == None : + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -250,21 +281,25 @@ class Eagle(object) : 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) + replystr = "" try: self._connect() + + if cmd == "get_history_data" : + self.soc.settimeout(45) 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: @@ -273,10 +308,12 @@ class Eagle(object) : except Exception: print("Unexpected error:", sys.exc_info()[0]) - print(ddat) + print "Error replystr = ", replystr replystr = None finally: self._disconnect() + if self.debug > 1 : + print "_send_comm replystr :\n", replystr return replystr def to_unix_time(self, t) : From 2d46b76d9dd7474490ffea8fbf2a2b8b80f7328f Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:33:28 -0800 Subject: [PATCH 07/17] may cause API to stop responding --- Tests/get_hist.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 Tests/get_hist.py diff --git a/Tests/get_hist.py b/Tests/get_hist.py new file mode 100755 index 0000000..c51ab96 --- /dev/null +++ b/Tests/get_hist.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +""" +based on TalkToEagle.py +""" + + +import socket +import sys +import time + +my_macid = "0xd8d5b90000001296" + + + +# Enter your Eagle's IP below +Eagle_IP = "10.1.1.39" + +## list_devices + +s = socket.create_connection( (Eagle_IP, 5002), 10) +print s +time.sleep(1) + +sendstr = "\nlist_devices\n\n" + +s.sendall(sendstr) +print +print "sending to Eagle: \n\r" +print sendstr +print + +time.sleep(1) + +print "Eagle response: \n\r" + +while 1: + buf = s.recv(1000) + if not buf: + break + sys.stdout.write(buf) + +s.close() + + +## get_history_data + +s = socket.create_connection( (Eagle_IP, 5002), 10) +print s + +time.sleep(1) + +sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) + +s.sendall(sendstr) +print +print "sending to Eagle: \n\r" +print sendstr +print + +time.sleep(1) + +print "Eagle response: \n\r" + +while 1: + buf = s.recv(1000) + if not buf: + break + sys.stdout.write(buf) + +s.close() + + +exit(0) From 1b145369e451293046c64572b12aa259602af432 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 17:56:06 -0800 Subject: [PATCH 08/17] clean up --- Tests/get_hist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/get_hist.py b/Tests/get_hist.py index c51ab96..a664293 100755 --- a/Tests/get_hist.py +++ b/Tests/get_hist.py @@ -50,7 +50,7 @@ print s time.sleep(1) -sendstr = "\nget_history_data\n\n0x00000000\n\n".format(my_macid) +sendstr = "\nget_history_data\n{0}\n0x00000000\n\n".format(my_macid) s.sendall(sendstr) print From 0e172fea6524b07d7e84b4110a3877fbf696aa30 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 4 Mar 2014 18:01:41 -0800 Subject: [PATCH 09/17] debug and docs --- RainEagle/EagleClass.py | 43 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 5d94be4..4341de2 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -83,17 +83,26 @@ class Eagle(object) : 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.timeout = kwargs.get("timeout", 10) self.soc = None self.macid = None + if self.debug : + print "Addr : = ", self.addr + print "timeout : = ", self.timeout + print "debug : = ", self.debug + # preload if self.getmac : self.device_info = self.list_devices() + if self.device_info == None : + raise IOError("Error connecting") if self.debug : + print "__init__ ", pprint(self.device_info) # self.macid = self.device_info['DeviceInfo']['DeviceMacId'] if self.debug : - print "DeviceMacId = ", self.macid + print "Init DeviceMacId = ", self.macid @@ -103,6 +112,8 @@ class Eagle(object) : comm_responce = self._send_comm("list_devices") if self.debug : print "comm_responce =", comm_responce + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) if self.macid == None : @@ -115,6 +126,8 @@ class Eagle(object) : if macid == None : macid = self.macid comm_responce = self._send_comm("get_device_data", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -131,6 +144,8 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -151,6 +166,8 @@ class Eagle(object) : if frequency : kwargs["Frequency"] = frequency comm_responce = self._send_comm("get_demand_values", **kwargs) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -168,6 +185,8 @@ class Eagle(object) : macid = self.macid comm_responce = self._send_comm("get_summation_values", MacId=macid, Interval=interval ) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -191,6 +210,8 @@ class Eagle(object) : comm_responce = self._send_comm("get_instantaneous_demand", MacId=macid, Frequency=frequency, Duration=duration) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -206,6 +227,8 @@ class Eagle(object) : if macid == None : macid = self.macid comm_responce = self._send_comm("get_fast_poll_status", MacId=macid) + if comm_responce == None: + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -215,6 +238,12 @@ class Eagle(object) : 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 + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + StartTime the start of the history interval (default oldest sample) + EndTime the end of the history interval (default current time) + Frequency Requested number of seconds between samples. """ if macid == None : macid = self.macid @@ -224,6 +253,8 @@ class Eagle(object) : if frequency : kwargs["Frequency"] = frequency comm_responce = self._send_comm("get_history_data", **kwargs) + if comm_responce == None : + return None etree = ET.fromstring('' + comm_responce + '' ) rv = _et2d(etree) return rv @@ -250,21 +281,25 @@ class Eagle(object) : 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) + replystr = "" try: self._connect() + + if cmd == "get_history_data" : + self.soc.settimeout(45) 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: @@ -273,10 +308,12 @@ class Eagle(object) : except Exception: print("Unexpected error:", sys.exc_info()[0]) - print(ddat) + print "Error replystr = ", replystr replystr = None finally: self._disconnect() + if self.debug > 1 : + print "_send_comm replystr :\n", replystr return replystr def to_unix_time(self, t) : From da375a626378c0515f392ec885631041a91e3437 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sat, 8 Mar 2014 02:03:12 -0800 Subject: [PATCH 10/17] work in progress --- RainEagle/EagleClass.py | 272 +++++++++++++++++++++++++++++++++++----- 1 file changed, 241 insertions(+), 31 deletions(-) diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 4341de2..8b49cbc 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -9,13 +9,32 @@ import sys import os import time import xml.etree.ElementTree as ET +import urllib +import urllib2 +from math import floor +from urlparse import urlparse +import json + + from pprint import pprint - +api_arg_format = { + +} -__all__ = ['Eagle'] +__all__ = ['Eagle', 'to_unix_time'] + +def to_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) def _et2d(et) : @@ -60,6 +79,19 @@ def _et2d(et) : d[child.tag] = child.text return d + def _tohex(n, width=10) : + """ convert arg to string with hex representation if possible""" + if isinstance(n, str) : + if n.isdigit() : + return "{:#{width}x}".format(int(n), width=width) + else : + return n + if isinstance(n, (int, long) ) : + return "{:#{width}x}".format(n, width=width) + if isinstance(n, float) : + return "{:#{width}x}".format(int(n), width=width) + return n + # @@ -106,10 +138,10 @@ class Eagle(object) : -# commands as class funtions +# socket commands as class functions def list_devices(self): - comm_responce = self._send_comm("list_devices") + comm_responce = self._send_soc_comm("list_devices") if self.debug : print "comm_responce =", comm_responce if comm_responce == None: @@ -125,7 +157,7 @@ class Eagle(object) : """ 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) + comm_responce = self._send_soc_comm("get_device_data", MacId=macid) if comm_responce == None: return None etree = ET.fromstring('' + comm_responce + '' ) @@ -142,7 +174,7 @@ class Eagle(object) : """ if macid == None : macid = self.macid - comm_responce = self._send_comm("get_instantaneous_demand", + comm_responce = self._send_soc_comm("get_instantaneous_demand", MacId=macid) if comm_responce == None: return None @@ -162,10 +194,12 @@ class Eagle(object) : """ if macid == None : macid = self.macid + if interval not in ['hour', 'day', 'week' ] : + raise ValueError("set_time_source interval must be 'hour', 'day' or 'week' ") kwargs = {"MacId": macid, "Interval": interval} if frequency : - kwargs["Frequency"] = frequency - comm_responce = self._send_comm("get_demand_values", **kwargs) + kwargs["Frequency"] = str(frequency) + comm_responce = self._send_soc_comm("get_demand_values", **kwargs) if comm_responce == None: return None etree = ET.fromstring('' + comm_responce + '' ) @@ -183,7 +217,9 @@ class Eagle(object) : """ if macid == None : macid = self.macid - comm_responce = self._send_comm("get_summation_values", + if interval not in ['day', 'week', 'month', 'year'] : + raise ValueError("set_time_source interval must be 'day', 'week', 'month' or 'year'") + comm_responce = self._send_soc_comm("get_summation_values", MacId=macid, Interval=interval ) if comm_responce == None: return None @@ -203,12 +239,10 @@ class Eagle(object) : """ if macid == None : macid = self.macid - if isinstance(frequency, int) : - frequency = "{:#04x}".format(m) - if isinstance(duration, int) : - frequency = "{:#04x}".format(m) + frequency = _tohex(frequency, 4) + duration = _tohex(duration, 4) - comm_responce = self._send_comm("get_instantaneous_demand", + comm_responce = self._send_soc_comm("get_instantaneous_demand", MacId=macid, Frequency=frequency, Duration=duration) if comm_responce == None: return None @@ -226,7 +260,7 @@ class Eagle(object) : """ if macid == None : macid = self.macid - comm_responce = self._send_comm("get_fast_poll_status", MacId=macid) + comm_responce = self._send_soc_comm("get_fast_poll_status", MacId=macid) if comm_responce == None: return None etree = ET.fromstring('' + comm_responce + '' ) @@ -247,12 +281,13 @@ class Eagle(object) : """ if macid == None : macid = self.macid - kwargs = {"MacId": macid, "StartTime": starttime} + kwargs = {"MacId": macid,} + kwargs["StartTime"] = _tohex(starttime, 10) if endtime : - kwargs["EndTime"] = endtime + kwargs["EndTime"] = _tohex(endtime, 10) if frequency : - kwargs["Frequency"] = frequency - comm_responce = self._send_comm("get_history_data", **kwargs) + kwargs["Frequency"] = _tohex(endtime, 6) + comm_responce = self._send_soc_comm("get_history_data", **kwargs) if comm_responce == None : return None etree = ET.fromstring('' + comm_responce + '' ) @@ -260,7 +295,165 @@ class Eagle(object) : return rv - # Support functions +# http commands as class functions + + def get_setting_data(self) : + comm_responce = self._send_http_comm("get_setting_data") + return comm_responce + + def get_device_config(self) : + comm_responce = self._send_http_comm("get_device_config") + return comm_responce + + def get_timezone(self) : + comm_responce = self._send_http_comm("get_timezone") + return comm_responce + + def get_time_source(self, macid=None) : + comm_responce = self._send_http_comm("get_time_source") + return comm_responce + + def set_remote_management(self, macid=None, status=None) : + """ set_remote_management + enabling ssh & vpn + + args: + status yes|no + + """ + if status not in ['yes', 'no'] : + raise ValueError("set_remote_management status must be 'yes' or 'no'") + comm_responce = self._send_http_comm("set_remote_management", Status=status) + return comm_responce + + + def set_time_source(self, macid=None, source=None) : + """ set_time_source + set time source + + args: + source meter|internet + """ + if status not in ['meter', 'internet'] : + raise ValueError("set_time_source Source must be 'meter' or 'internet'") + comm_responce = self._send_http_comm("set_time_source", Source=source) + return comm_responce + + def get_price(self) : + """ + get price for kWh + """ + comm_responce = self._send_http_comm("get_price") + return comm_responce + + def set_price(self, price) : + """ + Set price manualy + + args: + price Price/kWh + """ + #if isinstance(price, str) : + # price = float(price.lstrip('$')) + + if not isinstance(price, (int, long, float) ) : + raise ValueError("set_price price arg must me a int, long or float") + + trailing_digits = 0 + multiplier = 1 + while (((price * multiplier) != (floor(price * multiplier))) and (trailing_digits < 7) ) : + trailing_digits += 1 + multiplier *= 10 + + price_adj = "{:#x}".format( int(price * multiplier) ) + tdigits = "{:#x}".format( trailing_digits ) + + comm_responce = self._send_http_comm("set_price", Price=price_adj, TrailingDigits=tdigits) + return comm_responce + + + def set_price_auto(self) : + """ + Set Price from Meter + """ + comm_responce = self._send_http_comm("set_price", + Price="0xFFFFFFFF", + TrailingDigits="0x00") + return comm_responce + + def factory_reset(self) : + """ + Factory Reset + """ + comm_responce = self._send_http_comm("factory_reset") + return comm_responce + + +# def disconnect_meter(self) : +# """ +# disconnect from Smart Meter +# """ +# comm_responce = self._send_http_comm("disconnect_meter") +# return comm_responce + + + + def cloud_reset(self) : + """ + cloud_reset : Clear Cloud Configuration + """ + comm_responce = self._send_http_comm("cloud_reset") + return comm_responce + + + def set_cloud(self, url) : + """ + set cloud Url + """ + if url.__len__() > 200 : + raise ValueError("Max URL length is 200 characters long.\n") + + urlp = urlparse(url) + + if urlp.port : + port = "{:#4x}".format(urlp.port) + else : + port = "0x00" + + hostname = urlp.hostname + + if urlp.scheme : + protocol = urlp.scheme + else : + protocol = "http" + + url = urlp.path + + + if urlp.username : + userid = urlp.username + else : + userid = "" + + if urlp.password : + password = urlp.password + else : + password = "" + + comm_responce = self._send_http_comm("set_cloud", + Provider="manual", + Protocol=protocol, HostName=hostname, + Url=url, Port=port, + AuthCode="", Email="", + UserId=userid, Password=password) + + return comm_responce + + + + + +# Support functions def _connect(self) : self.soc = socket.create_connection( (self.addr, self.port), 10) @@ -274,7 +467,30 @@ class Eagle(object) : pass - def _send_comm(self, cmd, **kwargs): + def _send_http_comm(self, cmd, **kwargs): + + print "\n\n_send_http_comm : ", cmd + + commstr = "\n" + commstr += "{0!s}\n".format(cmd) + commstr += "{0!s}\n".format(self.macid) + for k, v in kwargs.items() : + commstr += "<{0}>{1!s}\n".format(k, v) + commstr += "\n" + + print(commstr) + + url = "http://{0}/cgi-bin/cgi_manager".format(self.addr) + + req = urllib2.Request(url, commstr) + response = urllib2.urlopen(req) + the_page = response.read() + + return the_page + + + + def _send_soc_comm(self, cmd, **kwargs): if cmd == "set_fast_poll" : command_tag = "RavenCommand" @@ -288,6 +504,7 @@ class Eagle(object) : commstr += "<{0}>{1!s}\n".format(k, v) commstr += "\n".format(command_tag) replystr = "" + # buf_list = [] try: self._connect() @@ -305,6 +522,8 @@ class Eagle(object) : if not buf: break replystr += buf + #buf_list.append(buf) + # replystr = ''.join(buf_list) except Exception: print("Unexpected error:", sys.exc_info()[0]) @@ -313,18 +532,9 @@ class Eagle(object) : finally: self._disconnect() if self.debug > 1 : - print "_send_comm replystr :\n", replystr + print "_send_soc_comm replystr :\n", replystr 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) From 9ba1d1729330eb7d5d8931b900f56b64228c0d5d Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sat, 8 Mar 2014 02:17:17 -0800 Subject: [PATCH 11/17] nothing --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cd89388..91bb707 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ Sav/* Bak/* *bak +WWW/* + # C extensions *.so From ef04000c56325d7288a82de1ba56bc59e8b96326 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sun, 9 Mar 2014 19:32:49 -0700 Subject: [PATCH 12/17] Funtional but Still a Work in progress --- RainEagle/EagleClass.py | 55 +++++++++++++------ RainEagle/__init__.py | 5 +- Tests/get_hist.py | 53 +++++++++++++++++- bin/get_meter_status.py | 116 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 19 deletions(-) create mode 100644 bin/get_meter_status.py diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index 8b49cbc..b2c128c 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -24,9 +24,19 @@ api_arg_format = { } -__all__ = ['Eagle', 'to_unix_time'] +__all__ = ['Eagle', 'to_epoch_1970, to_epoch_2000'] -def to_unix_time(t) : +def to_epoch_2000(t) : + """ converts time stored as + to unix's epoch of 1970 + offset in seconds from "Jan 1 00:00:00 2000" + """ + if isinstance(t, time.struct_time ) : + t = time.mktime(t) + return t - 946684800 + + +def to_epoch_1970(t) : """ converts time stored as offset in seconds from "Jan 1 00:00:00 2000" to unix's epoch of 1970 @@ -79,18 +89,18 @@ def _et2d(et) : d[child.tag] = child.text return d - def _tohex(n, width=10) : - """ convert arg to string with hex representation if possible""" - if isinstance(n, str) : - if n.isdigit() : - return "{:#{width}x}".format(int(n), width=width) - else : - return n - if isinstance(n, (int, long) ) : - return "{:#{width}x}".format(n, width=width) - if isinstance(n, float) : +def _tohex(n, width=10) : + """ convert arg to string with hex representation if possible""" + if isinstance(n, str) : + if n.isdigit() : return "{:#{width}x}".format(int(n), width=width) - return n + else : + return n + if isinstance(n, (int, long) ) : + return "{:#{width}x}".format(n, width=width) + if isinstance(n, float) : + return "{:#{width}x}".format(int(n), width=width) + return n @@ -298,18 +308,31 @@ class Eagle(object) : # http commands as class functions def get_setting_data(self) : + """ + get settings + """ comm_responce = self._send_http_comm("get_setting_data") return comm_responce def get_device_config(self) : + """ + get configs + """ comm_responce = self._send_http_comm("get_device_config") return comm_responce def get_timezone(self) : + """ + get current timezone configuration + """ comm_responce = self._send_http_comm("get_timezone") return comm_responce def get_time_source(self, macid=None) : + """ + get time source for device + retrrns value "meter" or "nternet" + """ comm_responce = self._send_http_comm("get_time_source") return comm_responce @@ -456,7 +479,7 @@ class Eagle(object) : # Support functions def _connect(self) : - self.soc = socket.create_connection( (self.addr, self.port), 10) + self.soc = socket.create_connection( (self.addr, self.port), self.timeout) def _disconnect(self): try : @@ -509,8 +532,8 @@ class Eagle(object) : try: self._connect() - if cmd == "get_history_data" : - self.soc.settimeout(45) + # if cmd == "get_history_data" : + # self.soc.settimeout(45) self.soc.sendall(commstr) if self.debug : print "commstr : \n", commstr diff --git a/RainEagle/__init__.py b/RainEagle/__init__.py index 34337b7..2fa823b 100644 --- a/RainEagle/__init__.py +++ b/RainEagle/__init__.py @@ -14,10 +14,11 @@ __copyright__ = "Copyright (C) 2014 Peter Shipley" __license__ = "BSD" -from EagleClass import Eagle +import EagleClass +from EagleClass import Eagle, to_epoch_1970, to_epoch_2000 #from RainEagle.EagleClass import Eagle -__all__ = ['Eagle'] +__all__ = ['Eagle', 'to_epoch_1970', 'to_epoch_2000'] diff --git a/Tests/get_hist.py b/Tests/get_hist.py index a664293..e2a57a6 100755 --- a/Tests/get_hist.py +++ b/Tests/get_hist.py @@ -8,6 +8,7 @@ based on TalkToEagle.py import socket import sys import time +import xml.etree.ElementTree as ET my_macid = "0xd8d5b90000001296" @@ -16,6 +17,43 @@ my_macid = "0xd8d5b90000001296" # Enter your Eagle's IP below Eagle_IP = "10.1.1.39" +def print_summ(cs) : + # global last_delivered + # time_stamp = eg.to_epoch_1970(cs['TimeStamp']) + + _delivered = cs.find('SummationDelivered').text + _received = cs.find('SummationReceived').text + _multiplier = cs.find('Multiplier').text + _divisor = cs.find('Divisor').text + + # print "Multiplier=", _multiplier, "Divisor=", _divisor, "Delivered=", _delivered, "Received=", _received + + multiplier=int(_multiplier, 16) + divisor=int(_divisor, 16) + delivered=int(_delivered, 16) + received=int(_received, 16) + time_stamp = 946684800 + int(cs.find('TimeStamp').text, 16) + + # print "Multiplier=", multiplier, "Divisor=", divisor, "Delivered=", delivered, "Received=", received, "TimeStamp", time_stamp + + if multiplier == 0 : + multiplier=1 + + if divisor == 0 : + divisor=1 + + reading_received = received * multiplier / float (divisor ) + reading_delivered = delivered * multiplier / float (divisor ) + #reading_delta = (reading_delivered - last_delivered) + #last_delivered = reading_delivered + + print time.asctime(time.localtime(time_stamp)), " : ", reading_received, "\t", reading_delivered + + +def print_reading(eg, rd) : + for dat in rd['Reading'] : + time_stamp = time.asctime(time.localtime( to_epoch_1970(dat['TimeStamp']) ) ) + ## list_devices s = socket.create_connection( (Eagle_IP, 5002), 10) @@ -62,13 +100,26 @@ time.sleep(1) print "Eagle response: \n\r" +j=0 +buf_list = [] while 1: buf = s.recv(1000) if not buf: break - sys.stdout.write(buf) + buf_list.append(buf) + #sys.stdout.write(buf) + j = j + 1 + +result_xml = ''.join(buf_list) +print result_xml +etree = ET.fromstring(result_xml) +for cs in etree.iter('CurrentSummation'): + print_summ(cs) + +print "j =", j s.close() +#main() exit(0) diff --git a/bin/get_meter_status.py b/bin/get_meter_status.py new file mode 100644 index 0000000..43259e6 --- /dev/null +++ b/bin/get_meter_status.py @@ -0,0 +1,116 @@ +#!/usr/local/bin/python2.7 +""" + A simple script get current meter values +""" + +__author__ = "Peter Shipley" + +import sys +sys.path.append('/usr/home/shipley/Projects/Eagle') # temp + + +# import RainEagle +from RainEagle import Eagle, to_epoch_1970 +import time +from pprint import pprint +import json + +last_delivered = 0 +debug = 0 + + +def main() : + eg = Eagle( debug=debug , addr="10.1.1.39") + # timeout=45, + + # print "\nlist_devices :" + # r = eg.list_devices() + # print "pprint 2" + # pprint(r) + + + + print "\nget_device_data :" + r = eg.get_device_data() + print + + # pprint(r['InstantaneousDemand']) + print_instantdemand( r['InstantaneousDemand']) + print + + # pprint(r['CurrentSummation']) + print_currentsummation(r['CurrentSummation']) + print + + exit(0) + + +def print_currentsummation(cs) : + + multiplier=int(cs['Multiplier'], 16) + divisor=int(cs['Divisor'], 16) + delivered=int(cs['SummationDelivered'], 16) + received=int(cs['SummationReceived'], 16) + + if multiplier == 0 : + multiplier=1 + + if divisor == 0 : + divisor=1 + + reading_received = received * multiplier / float (divisor ) + reading_delivered = delivered * multiplier / float (divisor ) + + time_stamp = to_epoch_1970(cs['TimeStamp']) + + print time.asctime(time.localtime(time_stamp)), " : " + print "\tReceived=", reading_received, "Kw" + print "\tDelivered=", reading_delivered, "Kw" + print "\t\t", (reading_delivered - reading_received) + + +# print "{0}\t{1:.4f}\t{2:0.4f}\t{3:.4f}".format( +# time.strftime("%Y-%m-%d %H:%M:%S", time_struct), +# reading_received, +# reading_delivered, +# (reading_delivered - reading_received) ) + + +def print_instantdemand(idemand) : + + time_stamp = to_epoch_1970(idemand['TimeStamp']) + + multiplier=int(idemand['Multiplier'], 16) + divisor=int(idemand['Divisor'], 16) + demand=int(idemand['Demand'], 16) + + # print "Multiplier=", multiplier, "Divisor=", divisor, "Demand=", demand + + if multiplier == 0 : + multiplier=1 + + if divisor == 0 : + divisor=1 + + reading = demand * multiplier / float (divisor ) + + print time.asctime(time.localtime(time_stamp)), " : " + print "\tDemand=", reading, "Kw" + print "\tAmps={:.3f}".format( ((reading * 1000) / 240) ) + + + +def print_reading(eg, rd) : + for dat in rd['Reading'] : + the_time = time.asctime(time.localtime( to_epoch_1970(dat['TimeStamp']) ) ) + print the_time, "Type=", dat['Type'], "Value=", dat['Value'] + +# +if __name__ == "__main__": + # import __main__ + # print(__main__.__file__) + # print("syntax ok") + main() + exit(0) + + From 36fed169c0e7805f119df28fc41266e719ac0a72 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sun, 9 Mar 2014 19:38:58 -0700 Subject: [PATCH 13/17] more works in progress --- bin/plot_power.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 50 +++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100755 bin/plot_power.py create mode 100644 setup.py diff --git a/bin/plot_power.py b/bin/plot_power.py new file mode 100755 index 0000000..0932b67 --- /dev/null +++ b/bin/plot_power.py @@ -0,0 +1,136 @@ +#!/usr/local/bin/python2.7 +""" + A simple script to generate guuplot data from meter history +""" + +__author__ = "Peter Shipley" + + +import sys +sys.path.append('/usr/home/shipley/Projects/Eagle') # temp + +import RainEagle +import time +from pprint import pprint +from RainEagle import Eagle, to_epoch_1970 + +import json + +last_delivered = 0 +last_received = 0 +max_delta_received = 0 +max_delta_delivered = 0 +day_delta_received = 0 +day_delta_delivered = 0 +curr_day=-1 + +def main(eg) : + # print_header() + print_data(eg) + # print_footer() + exit(0) + +def print_header() : + print """ +set terminal png size 2600,500 +set datafile separator "\t" +set xlabel "time" +set ylabel "power" +set grid +set key off +set pointsize 0.5 +set xtics 3600 +set xdata time +set timefmt "%Y-%m-%d %H:%M:%S" +set format x "%a %b %d %H:%M" +#set xrange [ "2014-03-04 00:00:00" : ] +# set xrange [ "2014-03-04 00:00:00" : "2014-03-09 17:20:00" ] +set style data lines +set autoscale y +set title "Power Use" +set output "poweruse.png" +plot "-" using 1:3 t "inbound" w lines, "-" using 1:5 t "outbound" +""" + +def print_footer() : + pass + +def print_data(eg) : + rh = eg.get_history_data() + #+ # endtime=None, frequency=None ) : + + for dat in rh['HistoryData']['CurrentSummation'] : + print_currentsummation(dat) + + print "# day_delta_received={0:0.4f}\tday_delta_delivered={1:0.4f} : {2:0.4f}".format(day_delta_received, day_delta_delivered, ( day_delta_delivered - day_delta_received ) ) + print "# max_delta_received={0:0.4f}\tmax_delta_delivered={1:0.4f}".format(max_delta_received, max_delta_delivered) + +def print_currentsummation(cs) : + global last_delivered + global last_received + + global max_delta_received + global max_delta_delivered + global day_delta_received + global day_delta_delivered + global curr_day + + time_stamp = to_epoch_1970(cs['TimeStamp']) + + multiplier=int(cs['Multiplier'], 16) + divisor=int(cs['Divisor'], 16) + delivered=int(cs['SummationDelivered'], 16) + received=int(cs['SummationReceived'], 16) + + # print "Multiplier=", multiplier, "Divisor=", divisor, "Demand=", demand + + if multiplier == 0 : + multiplier=1 + + if divisor == 0 : + divisor=1 + + reading_received = received * multiplier / float (divisor ) + delta_received = (reading_received - last_received) + last_received = reading_received + if ( delta_received > max_delta_received and delta_received < 1000) : + max_delta_received = delta_received + #print "new max_delta_received :", max_delta_received + + + reading_delivered = delivered * multiplier / float (divisor ) + delta_delivered = (reading_delivered - last_delivered) + last_delivered = reading_delivered + if ( delta_delivered > max_delta_delivered and delta_delivered < 1000) : + max_delta_delivered = delta_delivered + #print "\t\tnew max_delta_delivered :", max_delta_delivered + + + + + time_struct = time.localtime(time_stamp) + if curr_day != time_struct.tm_mday : + curr_day = time_struct.tm_mday + print "# day_delta_received={0:0.4f}\tday_delta_delivered={1:0.4f} : {2:0.4f}".format(day_delta_received, day_delta_delivered, ( day_delta_delivered - day_delta_received ) ) + day_delta_received = 0 + day_delta_delivered = 0 + + day_delta_received += delta_received + day_delta_delivered += delta_delivered + + print "{0}\t{1:.4f}\t{2:0.4f}\t{3:.4f}\t{4:0.4f}".format( + time.strftime("%Y-%m-%d %H:%M:%S", time_struct), + reading_received, + delta_received, + reading_delivered, + delta_delivered) + + + +if __name__ == "__main__": + # import __main__ + # print(__main__.__file__) + # print("syntax ok") + reagle = RainEagle.Eagle( debug=0 , addr="10.1.1.39") + main(reagle) + exit(0) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..035f6c1 --- /dev/null +++ b/setup.py @@ -0,0 +1,50 @@ + +# python setup.py --dry-run --verbose install + +import os.path +from setuptools import setup, find_packages +from distutils.command.install_scripts import install_scripts + +from distutils.core import setup + +class install_scripts_and_symlinks(install_scripts): + '''Like install_scripts, but also replicating nonexistent symlinks''' + def run(self): + print "=============install_scripts_and_symlinks run" + install_scripts.run(self) + # Replicate symlinks if they don't exist + print self + print "data_files = ", dir( self.distribution.data_files) + print type(self.distribution.data_files) + print self.distribution.data_files + for script in self.distribution.scripts: + print "\n---script = ",script + if os.path.islink(script): + target = os.readlink(script) + newlink = os.path.join(self.install_dir, os.path.basename(script)) + if not os.path.exists(newlink): + print "++++++++++", target, " -> ", newlink + # os.symlink(target, newlink) + + +setup( + name='RainEagle', + version='0.1.6', + author='Peter Shipley', + author_email='Peter.Shipley@gmail.com', + packages=find_packages(), + scripts=[ 'binbin/get_meter_status.py', 'bin/plot_power.py' ] + data_files=[ +# ('examples', ['bin/isy_find.py', 'bin/isy_progs.py', +# 'bin/isy_log.py', 'bin/isy_net_wol.py']), + ('bin', ['bin/isy_nodes.py', 'bin/isy_var.py']) ], + url='https://github.com/evilpete/RainEagle', + license='BSD', + description='Python Class for utilizing the Rainforest Automation Eagle ( RFA-Z109 ) socket API.', + long_description=open('README.md').read(), + cmdclass = { 'install_scripts': install_scripts_and_symlinks } +) + + + + From 90158725513fb4db248a2d7f2dc90dd6740f113b Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sun, 9 Mar 2014 19:45:44 -0700 Subject: [PATCH 14/17] automated docs --- doc.txt | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 doc.txt diff --git a/doc.txt b/doc.txt new file mode 100644 index 0000000..d9dea27 --- /dev/null +++ b/doc.txt @@ -0,0 +1,149 @@ +Help on module EagleClass: + +NAME + EagleClass + +FILE + RainEagle/EagleClass.py + +CLASSES + __builtin__.object + Eagle + + class Eagle(__builtin__.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 ) + | + | Methods defined here: + | + | __init__(self, **kwargs) + | + | cloud_reset(self) + | cloud_reset : Clear Cloud Configuration + | + | factory_reset(self) + | Factory Reset + | + | 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 + | + | get_device_config(self) + | get configs + | + | get_device_data(self, macid=None) + | Send the GET_DEVICE_DATA command to get a data dump + | + | 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 + | + | 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 + | + | args: + | MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + | StartTime the start of the history interval (default oldest sample) + | EndTime the end of the history interval (default current time) + | Frequency Requested number of seconds between samples. + | + | 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 + | + | get_price(self) + | get price for kWh + | + | get_setting_data(self) + | get settings + | + | 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 + | + | get_time_source(self, macid=None) + | get time source for device + | retrrns value "meter" or "nternet" + | + | get_timezone(self) + | get current timezone configuration + | + | list_devices(self) + | + | set_cloud(self, url) + | set cloud Url + | + | 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) + | + | set_price(self, price) + | Set price manualy + | + | args: + | price Price/kWh + | + | set_price_auto(self) + | Set Price from Meter + | + | set_remote_management(self, macid=None, status=None) + | set_remote_management + | enabling ssh & vpn + | + | args: + | status yes|no + | + | set_time_source(self, macid=None, source=None) + | set_time_source + | set time source + | + | args: + | source meter|internet + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables (if defined) + | + | __weakref__ + | list of weak references to the object (if defined) + +DATA + __all__ = ['Eagle', 'to_epoch_1970, to_epoch_2000'] + __author__ = 'Peter Shipley ' + __copyright__ = 'Copyright (C) 2014 Peter Shipley' + __license__ = 'BSD' + +AUTHOR + Peter Shipley + + From d1ff4b9835276701b127137787e9cae7155dfc26 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Sun, 9 Mar 2014 20:21:25 -0700 Subject: [PATCH 15/17] added web API calls --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 012ea99..a571b29 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ API Call list : ```python + # Socket API based commands list_devices() get_demand_values(macid=None, interval='hour', frequency=None) get_device_data(macid=None) @@ -27,10 +28,25 @@ API Call list : get_fast_poll_status(macid=None) set_fast_poll(macid=None, frequency='0x04', duration='0xFF') + + # Web API based calls + def get_setting_data(macid=None) + def get_device_config(macid=None) + def get_timezone(macid=None) + def get_time_source(macid=None) + def set_remote_management(macid=None, status="Yes") + def set_time_source(macid=None, source="internet") + def get_price(macid=None) + def set_price(macid=None, price) + def set_price_auto(macid=None) + def factory_reset(macid=None) + def cloud_reset(macid=None) + def set_cloud(macid=None, url) + ``` -API Calls return dictionarys containing data results -returns None if error +API Calls return dictionarys containing data results, +raises exception or returns None if error ## External Documentation From 70e6eb21dc24901adb1f11dbb6f5b64de5ebb08f Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 11 Mar 2014 03:00:58 -0700 Subject: [PATCH 16/17] added calls for http/ajax funtions --- RainEagle/EagleClass.py | 1056 ++++++++++++++++++++++++--------------- 1 file changed, 657 insertions(+), 399 deletions(-) diff --git a/RainEagle/EagleClass.py b/RainEagle/EagleClass.py index b2c128c..984d25b 100644 --- a/RainEagle/EagleClass.py +++ b/RainEagle/EagleClass.py @@ -1,7 +1,7 @@ __author__ = 'Peter Shipley ' -__copyright__ = "Copyright (C) 2014 Peter Shipley" +__copyright__ = "Copyright (C) 2014 Peter Shipley" __license__ = "BSD" import socket @@ -14,464 +14,722 @@ import urllib2 from math import floor from urlparse import urlparse import json - +from warnings import warn from pprint import pprint - + api_arg_format = { } -__all__ = ['Eagle', 'to_epoch_1970, to_epoch_2000'] +__all__ = ['Eagle', 'RainEagleResponseError', 'to_epoch_1970, to_epoch_2000'] + +class RainEagleResponseError(RuntimeError): + """General exception for responce errors + from Rainforest Automation EAGLE (RFA-Z109) + """ + pass + + def to_epoch_2000(t) : """ converts time stored as - to unix's epoch of 1970 - offset in seconds from "Jan 1 00:00:00 2000" + to unix's epoch of 1970 + offset in seconds from "Jan 1 00:00:00 2000" """ if isinstance(t, time.struct_time ) : - t = time.mktime(t) + t = time.mktime(t) return t - 946684800 def to_epoch_1970(t) : """ converts time stored as - offset in seconds from "Jan 1 00:00:00 2000" - to unix's epoch of 1970 + 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 + return t + 946684800 if isinstance(t, str) and t.startswith('0x') : - return 946684800 + int(t, 16) + return 946684800 + int(t, 16) def _et2d(et) : """ Etree to Dict - converts an ETree to a Dict Tree - lists are created for duplicate tag + converts an ETree to a Dict Tree + lists are created for duplicate tag - if there are multiple XML of the name name - an list array is used - attrib tags are converted to "tag_name" + "attrib_name" + if there are multiple XML of the name name + an list array is used + attrib tags are converted to "tag_name" + "attrib_name" - if an invalid arg is passed a empty dict is retrurned + if an invalid arg is passed a empty dict is retrurned - arg: ETree Element obj + arg: ETree Element obj - returns: a dict obj + returns: a dict obj """ d = dict() if not isinstance(et, ET.Element) : - return d + return d children = list(et) if et.attrib : - for k, v in list(et.items()) : - d[et.tag + "-" + k] = v + for k, v in list(et.items()) : + d[et.tag + "-" + k] = v if children : - for child in children : - if child.tag in d : - if type(d[child.tag]) != list : - t = d[child.tag] - d[child.tag] = [t] - if list(child) or child.attrib : - if child.tag in d : - d[child.tag].append(_et2d(child)) - else : - d[child.tag] = _et2d(child) - else : - if child.tag in d : - d[child.tag].append(child.text) - else : - d[child.tag] = child.text + for child in children : + if child.tag in d : + if type(d[child.tag]) != list : + t = d[child.tag] + d[child.tag] = [t] + if list(child) or child.attrib : + if child.tag in d : + d[child.tag].append(_et2d(child)) + else : + d[child.tag] = _et2d(child) + else : + if child.tag in d : + d[child.tag].append(child.text) + else : + d[child.tag] = child.text return d -def _tohex(n, width=10) : - """ convert arg to string with hex representation if possible""" - if isinstance(n, str) : - if n.isdigit() : - return "{:#{width}x}".format(int(n), width=width) - else : - return n - if isinstance(n, (int, long) ) : - return "{:#{width}x}".format(n, width=width) - if isinstance(n, float) : - return "{:#{width}x}".format(int(n), width=width) - return n +def twos_comp(val, bits=32): + """compute the 2's compliment of int value val""" + if( (val&(1<<(bits-1))) != 0 ): + val = val - (1< 2147483647) or ( i < -2147483648 ) : + warn("_tohex : signed int to large (" + str(n) + ")\n", RuntimeWarning, stacklevel=2) + + if i < 0 : + i += 0x100000000 + + return "{:#0{width}x}".format(int(i), width=width) # class Eagle(object) : """ - Class for talking to Rainforest Automation EAGLE (RFA-Z109) + 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) + 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 ) + Currently there is very little error handling ( if any at all ) """ def __init__(self, **kwargs): - self.debug = kwargs.get("debug", 0) - - if self.debug : - 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.timeout = kwargs.get("timeout", 10) - self.soc = None - self.macid = None - - if self.debug : - print "Addr : = ", self.addr - print "timeout : = ", self.timeout - print "debug : = ", self.debug - - # preload - if self.getmac : - self.device_info = self.list_devices() - if self.device_info == None : - raise IOError("Error connecting") - if self.debug : - print "__init__ ", - pprint(self.device_info) - # self.macid = self.device_info['DeviceInfo']['DeviceMacId'] - if self.debug : - print "Init DeviceMacId = ", self.macid + self.debug = kwargs.get("debug", 0) + + if self.debug : + 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.timeout = kwargs.get("timeout", 10) + self.soc = None + self.macid = None + + if self.debug : + print "Addr : = ", self.addr + print "timeout : = ", self.timeout + print "debug : = ", self.debug + + # preload + if self.getmac : + self.device_info = self.list_devices() + if self.device_info is None : + raise IOError("Error connecting") + if self.debug : + print "__init__ ", + pprint(self.device_info) + # self.macid = self.device_info['DeviceInfo']['DeviceMacId'] + if self.debug : + print "Init DeviceMacId = ", self.macid # socket commands as class functions def list_devices(self): - comm_responce = self._send_soc_comm("list_devices") - if self.debug : - print "comm_responce =", comm_responce - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - if self.macid == None : - self.macid = rv['DeviceInfo']['DeviceMacId'] - return rv - - # 3 + comm_responce = self._send_soc_comm("list_devices") + if self.debug : + print "comm_responce =", comm_responce + if comm_responce is None: + raise RainEagleResponseError("list_devices : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + if self.macid is None : + self.macid = rv['DeviceInfo']['DeviceMacId'] + return rv + + # 3 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_soc_comm("get_device_data", MacId=macid) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ Send the GET_DEVICE_DATA command to get a data dump """ + if macid is None : + macid = self.macid + comm_responce = self._send_soc_comm("get_device_data", MacId=macid) + if comm_responce is None: + raise RainEagleResponseError("get_device_data : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 10 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_soc_comm("get_instantaneous_demand", - MacId=macid) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ 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 is None : + macid = self.macid + comm_responce = self._send_soc_comm("get_instantaneous_demand", + MacId=macid) + if comm_responce is None: + raise RainEagleResponseError("get_instantaneous_demand : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 11 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 - if interval not in ['hour', 'day', 'week' ] : - raise ValueError("set_time_source interval must be 'hour', 'day' or 'week' ") - kwargs = {"MacId": macid, "Interval": interval} - if frequency : - kwargs["Frequency"] = str(frequency) - comm_responce = self._send_soc_comm("get_demand_values", **kwargs) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ 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 is None : + macid = self.macid + if interval not in ['hour', 'day', 'week' ] : + raise ValueError("get_demand_values interval must be 'hour', 'day' or 'week' ") + kwargs = {"MacId": macid, "Interval": interval} + if frequency : + kwargs["Frequency"] = str(frequency) + comm_responce = self._send_soc_comm("get_demand_values", **kwargs) + if comm_responce is None: + raise RainEagleResponseError("get_demand_values : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 12 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 - if interval not in ['day', 'week', 'month', 'year'] : - raise ValueError("set_time_source interval must be 'day', 'week', 'month' or 'year'") - comm_responce = self._send_soc_comm("get_summation_values", - MacId=macid, Interval=interval ) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ 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 is None : + macid = self.macid + if interval not in ['day', 'week', 'month', 'year'] : + raise ValueError("get_summation_values interval must be 'day', 'week', 'month' or 'year'") + comm_responce = self._send_soc_comm("get_summation_values", + MacId=macid, Interval=interval ) + if comm_responce is None: + raise RainEagleResponseError("get_summation_values : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 14 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 - frequency = _tohex(frequency, 4) - duration = _tohex(duration, 4) - - comm_responce = self._send_soc_comm("get_instantaneous_demand", - MacId=macid, Frequency=frequency, Duration=duration) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ 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 is None : + macid = self.macid + frequency = _tohex(frequency, 2) + duration = _tohex(duration, 2) + + comm_responce = self._send_soc_comm("get_instantaneous_demand", + MacId=macid, Frequency=frequency, Duration=duration) + if comm_responce is None: + raise RainEagleResponseError("set_fast_poll : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 15 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_soc_comm("get_fast_poll_status", MacId=macid) - if comm_responce == None: - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv + """ 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 is None : + macid = self.macid + comm_responce = self._send_soc_comm("get_fast_poll_status", MacId=macid) + if comm_responce is None: + return None + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # 17 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 - - args: - MacId 16 hex digits, MAC addr of EAGLE ZigBee radio - StartTime the start of the history interval (default oldest sample) - EndTime the end of the history interval (default current time) - Frequency Requested number of seconds between samples. - """ - if macid == None : - macid = self.macid - kwargs = {"MacId": macid,} - kwargs["StartTime"] = _tohex(starttime, 10) - if endtime : - kwargs["EndTime"] = _tohex(endtime, 10) - if frequency : - kwargs["Frequency"] = _tohex(endtime, 6) - comm_responce = self._send_soc_comm("get_history_data", **kwargs) - if comm_responce == None : - return None - etree = ET.fromstring('' + comm_responce + '' ) - rv = _et2d(etree) - return rv - + """ Send the GET_HISTORY_DATA command + get a series of summation values over an interval of time + ( socket command api ) + + args: + MacId 16 hex digits, MAC addr of EAGLE ZigBee radio + StartTime the start of the history interval (default oldest sample) + EndTime the end of the history interval (default current time) + Frequency Requested number of seconds between samples. + """ + if macid is None : + macid = self.macid + kwargs = {"MacId": macid} + kwargs["StartTime"] = _tohex(starttime, 8) + if endtime : + kwargs["EndTime"] = _tohex(endtime, 8) + if frequency : + kwargs["Frequency"] = _tohex(endtime, 4) + comm_responce = self._send_soc_comm("get_history_data", **kwargs) + if comm_responce is None : + raise RainEagleResponseError("get_history_data : Null reply") + etree = ET.fromstring('' + comm_responce + '' ) + rv = _et2d(etree) + return rv # http commands as class functions + def get_uploaders(self) : + """ + gets list of uploaders for Web UI + + On Success returns dict with the values (example): + 'uploader[0]': 'none' + 'uploader[1]': 'bidgely' + 'uploader_name[0]': 'None' + 'uploader_name[1]': 'Bidgely Inc.' + + """ + comm_responce = self._send_http_comm("get_uploaders") + return json.loads(comm_responce) + + def get_uploader() : + """ + gets current uploaders config + + On Success returns dict with the values (example): + "uploader_timestamp" : "1394503703" + "uploader_provider" : "bidgely" + "uploader_protocol" : "https" + "uploader_hostname" : "api.bidgely.com" + "uploader_url" : "/v1/users/44441b47-1b9a-4a65-8e8c-0efefe05bb88/homes/1/gateways/1" + "uploader_port" : "0" + "uploader_auth_code" : "44441b47-1b9a-4a65-8e8c-0efefe05bb88" + "uploader_email" : "" + "uploader_user_id" : "" + "uploader_password" : "" + "uploader_enabled" : "Y" + + See also set_cloud() to set current uploader cloud config + """ + comm_responce = self._send_http_comm("get_uploader") + return json.loads(comm_responce) + + + def set_message_read(self) : + """ + On Success returns dict with the values : + 'remote_management_status' : 'success' + + """ + comm_responce = self._send_http_comm("set_message_read") + return json.loads(comm_responce) + + def confirm_message(self, id) : + """ + """ + id = _tohex(id) + comm_responce = self._send_http_comm("confirm_message", Id=id) + return json.loads(comm_responce) + + def get_message(self) : + """ + On Success returns dict with the values (example): + "meter_status" : "Connected" + "message_timestamp" : "946684800" + "message_text" : "" + "message_confirmed" : "N" + "message_confirm_required" : "N" + "message_id" : "0" + "message_queue" : "active" + "message_priority" : "" + "message_read" : "Y" + + """ + comm_responce = self._send_http_comm("get_message") + return json.loads(comm_responce) + + def get_usage_data(self) : + """ + Get current demand usage summation + + On Success returns dict with the values (example): + 'demand' : '0.4980', + 'demand_timestamp' : '1394505386', + 'demand_units' : 'kW', + 'message_confirm_required' : 'N', + 'message_confirmed' : 'N', + 'message_id' : '0', + 'message_priority' : '', + 'message_queue' : active', + 'message_read' : 'Y', + 'message_text' : '', + 'message_timestamp' : '946684800', + 'meter_status' : 'Connected', + 'price' : '0.1400', + 'price_label' : 'Set by User', + 'price_units' : '$', + 'summation_delivered' : '2667.867', + 'summation_received' : '37.283', + 'summation_units' : 'kWh', + 'usage_timestamp' : '1394505386'} + + """ + comm_responce = self._send_http_comm("get_usage_data") + return json.loads(comm_responce) + + + def get_historical_data_alt(self, period="day") : + """ + get a series of summation values over an interval of time + ( http command api ) + + args: + period day|week|month|year + + On Success returns dict with the values (example): + 'data_period' 'day', + 'data_size' '14', + 'timestamp[0]' '1394422200', + 'timestamp[1]' '1394425800', + 'timestamp[2]' '1394429400', + 'timestamp[3]' '1394433000', + 'timestamp[4]' '1394436600', + 'timestamp[5]' '1394440200', + 'timestamp[6]' '1394443800', + 'timestamp[7]' '1394447400', + 'timestamp[8]' '1394451000', + 'timestamp[9]' '1394454600', + 'timestamp[10]' '1394458200', + 'timestamp[11]' '1394461800', + 'timestamp[12]' '1394465400', + 'timestamp[13]' '1394469000', + 'value[0]' '0.429', + 'value[1]' '0.426', + 'value[2]' '0.422', + 'value[3]' '0.627', + 'value[4]' '0.735', + 'value[5]' '0.193', + 'value[6]' '0.026', + 'value[7]' '-0.985', + 'value[8]' '-1.491', + 'value[9]' '-2.196'} + 'value[11]' '-1.868', + 'value[12]' '-1.330', + 'value[13]' '-0.870', + + """ + if period not in ['day', 'week', 'month', 'year'] : + raise ValueError("get_historical_data_alt period must be one of day|week|month|year") + comm_responce = self._send_http_comm("get_historical_data", Period=period) + return json.loads(comm_responce) + + + def get_usage_data(self) : + """ + """ + comm_responce = self._send_http_comm("get_usage_data") + return json.loads(comm_responce) + def get_setting_data(self) : - """ - get settings - """ - comm_responce = self._send_http_comm("get_setting_data") - return comm_responce + """ + get settings data + + On Success returns dict with value containing setting + relating to price, uploader, network & device + + """ + comm_responce = self._send_http_comm("get_setting_data") + return json.loads(comm_responce) def get_device_config(self) : - """ - get configs - """ - comm_responce = self._send_http_comm("get_device_config") - return comm_responce + """ + get remote management status + + On Success returns dict with value 'Y' or 'N' : + 'config_ssh_enabled': 'Y' + 'config_vpn_enabled': 'Y' + + """ + comm_responce = self._send_http_comm("get_device_config") + return json.loads(comm_responce) + + def get_gateway_info(self) : + """ + gets network status + + On Success returns dict with the values (example): + 'gateway_cloud_id': '00:09:69' + 'gateway_internet_status': 'connected' + 'gateway_ip_addr': '10.11.12.13' + 'gateway_mac_id': 'D8:D5:B9:00:90:24' + + """ + comm_responce = self._send_http_comm("get_gateway_info") + return json.loads(comm_responce) def get_timezone(self) : - """ - get current timezone configuration - """ - comm_responce = self._send_http_comm("get_timezone") - return comm_responce + """ + get current timezone configuration + + On Success returns dict with the value : + 'timezone_localTime': '1394527011' + 'timezone_olsonName': 'UTC/GMT', + 'timezone_status': '2' + 'timezone_utcOffset': 'UTC' + 'timezone_utcTime': '1394527011' + 'timezone_status': 'success' + + + """ + comm_responce = self._send_http_comm("get_timezone") + return json.loads(comm_responce) def get_time_source(self, macid=None) : - """ - get time source for device - retrrns value "meter" or "nternet" - """ - comm_responce = self._send_http_comm("get_time_source") - return comm_responce + """ + get time source for device + + On Success returns dict with value 'internet' or 'meter' : + 'time_source': 'internet'} + """ + comm_responce = self._send_http_comm("get_time_source") + return json.loads(comm_responce) + + def get_remote_management(self) : + return get_device_config(self) + + def set_remote_management(self, macid=None, status="on") : + """ set_remote_management + enabling ssh & vpn - def set_remote_management(self, macid=None, status=None) : - """ set_remote_management - enabling ssh & vpn + args: + status on|off - args: - status yes|no + On Success returns dict with value : + 'remote_management_status': 'success' - """ - if status not in ['yes', 'no'] : - raise ValueError("set_remote_management status must be 'yes' or 'no'") - comm_responce = self._send_http_comm("set_remote_management", Status=status) - return comm_responce + """ + if status not in ['on', 'off'] : + raise ValueError("set_remote_management status must be 'on' or 'off'") + comm_responce = self._send_http_comm("set_remote_management", Status=status) + return json.loads(comm_responce) def set_time_source(self, macid=None, source=None) : - """ set_time_source - set time source + """ set_time_source + set time source + + args: + source meter|internet - args: - source meter|internet - """ - if status not in ['meter', 'internet'] : - raise ValueError("set_time_source Source must be 'meter' or 'internet'") - comm_responce = self._send_http_comm("set_time_source", Source=source) - return comm_responce + On Success returns dict with value : + 'set_time_source_status': u'success' + + On Error returns dict with value : + 'set_time_source_status': 'invalid source name' + """ + if source not in ['meter', 'internet'] : + raise ValueError("set_time_source Source must be 'meter' or 'internet'") + comm_responce = self._send_http_comm("set_time_source", Source=source) + return json.loads(comm_responce) def get_price(self) : - """ - get price for kWh - """ - comm_responce = self._send_http_comm("get_price") - return comm_responce + """ + get price for kWh + + On Success returns (example): + price': '0.1300', + price_label': 'Set by User' or '--' + price_timestamp': '1394524458', + price_units': '$'} + + returns empty dict on Error + """ + comm_responce = self._send_http_comm("get_price") + return json.loads(comm_responce) def set_price(self, price) : - """ - Set price manualy + """ + Set price manualy - args: - price Price/kWh - """ - #if isinstance(price, str) : - # price = float(price.lstrip('$')) + args: + price Price/kWh - if not isinstance(price, (int, long, float) ) : - raise ValueError("set_price price arg must me a int, long or float") + On Success returns dict with value : + 'set_price_status': 'success' + + """ + if isinstance(price, str) and price.startswith('$') : + price = float(price.lstrip('$')) - trailing_digits = 0 - multiplier = 1 - while (((price * multiplier) != (floor(price * multiplier))) and (trailing_digits < 7) ) : - trailing_digits += 1 - multiplier *= 10 + if not isinstance(price, (int, long, float) ) : + raise ValueError("set_price price arg must me a int, long or float") + if ( price <= 0 ): + raise ValueError("set_price price arg greater then 0") - price_adj = "{:#x}".format( int(price * multiplier) ) - tdigits = "{:#x}".format( trailing_digits ) + trailing_digits = 0 + multiplier = 1 + while (((price * multiplier) != (floor(price * multiplier))) and (trailing_digits < 7) ) : + trailing_digits += 1 + multiplier *= 10 - comm_responce = self._send_http_comm("set_price", Price=price_adj, TrailingDigits=tdigits) - return comm_responce + price_adj = "{:#x}".format( int(price * multiplier) ) + tdigits = "{:#x}".format( trailing_digits ) + + comm_responce = self._send_http_comm("set_price", Price=price_adj, TrailingDigits=tdigits) + return json.loads(comm_responce) def set_price_auto(self) : - """ - Set Price from Meter - """ - comm_responce = self._send_http_comm("set_price", - Price="0xFFFFFFFF", - TrailingDigits="0x00") - return comm_responce + """ + Set Price from Meter + + On Success returns dict with value : + 'set_price_status': 'success' + """ + comm_responce = self._send_http_comm("set_price", + Price="0xFFFFFFFF", + TrailingDigits="0x00") + return json.loads(comm_responce) + +# def set_multiplier_divisor(self, multiplier=1, divisor=1) : +# """ +# set multiplier and divisor manualy +# """ +# multiplier = _tohex(multiplier, 8) +# divisor = _tohex(divisor, 8) +# comm_responce = self._send_http_comm("set_multiplier_divisor", Multiplier=multiplier, Divisor=divisor) +# return json.loads(comm_responce) def factory_reset(self) : - """ - Factory Reset - """ - comm_responce = self._send_http_comm("factory_reset") - return comm_responce + """ + Factory Reset + """ + comm_responce = self._send_http_comm("factory_reset") + return json.loads(comm_responce) # def disconnect_meter(self) : -# """ -# disconnect from Smart Meter -# """ -# comm_responce = self._send_http_comm("disconnect_meter") -# return comm_responce +# """ +# disconnect from Smart Meter +# """ +# comm_responce = self._send_http_comm("disconnect_meter") +# return json.loads(comm_responce) def cloud_reset(self) : - """ - cloud_reset : Clear Cloud Configuration - """ - comm_responce = self._send_http_comm("cloud_reset") - return comm_responce + """ + cloud_reset : Clear Cloud Configuration + + """ + comm_responce = self._send_http_comm("cloud_reset") + return json.loads(comm_responce) + + def set_cloud(self, url, authcode="", email="") : + """ + set cloud Url - def set_cloud(self, url) : - """ - set cloud Url - """ - if url.__len__() > 200 : - raise ValueError("Max URL length is 200 characters long.\n") + args: + url Url for uploader + authcode + email - urlp = urlparse(url) + See also get_uploader() to retrieve current uploader cloud config + """ + if url.__len__() > 200 : + raise ValueError("Max URL length is 200 characters long.\n") - if urlp.port : - port = "{:#4x}".format(urlp.port) - else : - port = "0x00" + urlp = urlparse(url) - hostname = urlp.hostname + if urlp.port : + port = "{:#04x}".format(urlp.port) + else : + port = "0x00" - if urlp.scheme : - protocol = urlp.scheme - else : - protocol = "http" + hostname = urlp.hostname - url = urlp.path + if urlp.scheme : + protocol = urlp.scheme + else : + protocol = "http" + url = urlp.path - if urlp.username : - userid = urlp.username - else : - userid = "" + if urlp.username : + userid = urlp.username + else : + userid = "" - if urlp.password : - password = urlp.password - else : - password = "" + if urlp.password : + password = urlp.password + else : + password = "" - comm_responce = self._send_http_comm("set_cloud", - Provider="manual", - Protocol=protocol, HostName=hostname, - Url=url, Port=port, - AuthCode="", Email="", + comm_responce = self._send_http_comm("set_cloud", + Provider="manual", + Protocol=protocol, HostName=hostname, + Url=url, Port=port, + AuthCode=authcode, Email=email, UserId=userid, Password=password) - return comm_responce - + return json.loads(comm_responce) + @@ -479,7 +737,7 @@ class Eagle(object) : # Support functions def _connect(self) : - self.soc = socket.create_connection( (self.addr, self.port), self.timeout) + self.soc = socket.create_connection( (self.addr, self.port), self.timeout) def _disconnect(self): try : @@ -492,81 +750,81 @@ class Eagle(object) : def _send_http_comm(self, cmd, **kwargs): - print "\n\n_send_http_comm : ", cmd + print "\n\n_send_http_comm : ", cmd - commstr = "\n" - commstr += "{0!s}\n".format(cmd) - commstr += "{0!s}\n".format(self.macid) - for k, v in kwargs.items() : - commstr += "<{0}>{1!s}\n".format(k, v) - commstr += "\n" + commstr = "\n" + commstr += "{0!s}\n".format(cmd) + commstr += "{0!s}\n".format(self.macid) + for k, v in kwargs.items() : + commstr += "<{0}>{1!s}\n".format(k, v) + commstr += "\n" - print(commstr) + print(commstr) - url = "http://{0}/cgi-bin/cgi_manager".format(self.addr) + url = "http://{0}/cgi-bin/cgi_manager".format(self.addr) - req = urllib2.Request(url, commstr) - response = urllib2.urlopen(req) - the_page = response.read() + req = urllib2.Request(url, commstr) + response = urllib2.urlopen(req) + the_page = response.read() - return the_page + return the_page def _send_soc_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) - replystr = "" - # buf_list = [] - - try: - self._connect() - - # if cmd == "get_history_data" : - # self.soc.settimeout(45) - self.soc.sendall(commstr) - if self.debug : - print "commstr : \n", commstr - - # time.sleep(1) - - while 1 : - buf = self.soc.recv(1000) - if not buf: - break - replystr += buf - #buf_list.append(buf) - # replystr = ''.join(buf_list) - - except Exception: - print("Unexpected error:", sys.exc_info()[0]) - print "Error replystr = ", replystr - replystr = None - finally: - self._disconnect() - if self.debug > 1 : - print "_send_soc_comm replystr :\n", replystr - return replystr - - -# Do nothing -# (syntax check) -# + 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) + replystr = "" + # buf_list = [] + + try: + self._connect() + + # if cmd == "get_history_data" : + # self.soc.settimeout(45) + self.soc.sendall(commstr) + if self.debug : + print "commstr : \n", commstr + + # time.sleep(1) + + while 1 : + buf = self.soc.recv(1000) + if not buf: + break + replystr += buf + #buf_list.append(buf) + # replystr = ''.join(buf_list) + + except Exception: + print("Unexpected error:", sys.exc_info()[0]) + print "Error replystr = ", replystr + replystr = None + finally: + self._disconnect() + if self.debug > 1 : + print "_send_soc_comm replystr :\n", replystr + return replystr + + +# Do nothing +# (syntax check) +# if __name__ == "__main__": import __main__ - print(__main__.__file__) - - print("syntax ok") - exit(0) - + print(__main__.__file__) + + print("syntax ok") + exit(0) + From 386d11cd553ffa46cbc23282bb5b9641faecce46 Mon Sep 17 00:00:00 2001 From: Peter Shipley Date: Tue, 11 Mar 2014 03:01:58 -0700 Subject: [PATCH 17/17] cleanup --- RainEagle/__init__.py | 6 +- bin/get_meter_status.py | 16 ++- doc.txt | 237 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 241 insertions(+), 18 deletions(-) mode change 100644 => 100755 bin/get_meter_status.py diff --git a/RainEagle/__init__.py b/RainEagle/__init__.py index 2fa823b..c467a07 100644 --- a/RainEagle/__init__.py +++ b/RainEagle/__init__.py @@ -10,15 +10,15 @@ if sys.hexversion < 0x20703f0 : __revision__ = "$Id: 20140301 $" __version__ = '0.1.20140301' __author__ = 'Peter Shipley ' -__copyright__ = "Copyright (C) 2014 Peter Shipley" +__copyright__ = "Copyright (C) 2014 Peter Shipley" __license__ = "BSD" import EagleClass -from EagleClass import Eagle, to_epoch_1970, to_epoch_2000 +from EagleClass import Eagle, RainEagleResponseError, to_epoch_1970, to_epoch_2000 #from RainEagle.EagleClass import Eagle -__all__ = ['Eagle', 'to_epoch_1970', 'to_epoch_2000'] +__all__ = ['Eagle', 'RainEagleResponseError', 'to_epoch_1970', 'to_epoch_2000'] diff --git a/bin/get_meter_status.py b/bin/get_meter_status.py old mode 100644 new mode 100755 index 43259e6..6d8d6f8 --- a/bin/get_meter_status.py +++ b/bin/get_meter_status.py @@ -44,6 +44,11 @@ def main() : exit(0) +def twos_comp(val, bits=32): + """compute the 2's compliment of int value val""" + if( (val&(1<<(bits-1))) != 0 ): + val = val - (1< 0x7FFFFFFF: + demand -= 0x100000000 # print "Multiplier=", multiplier, "Divisor=", divisor, "Demand=", demand @@ -92,11 +100,11 @@ def print_instantdemand(idemand) : if divisor == 0 : divisor=1 - reading = demand * multiplier / float (divisor ) + reading = (demand * multiplier) / float (divisor ) print time.asctime(time.localtime(time_stamp)), " : " print "\tDemand=", reading, "Kw" - print "\tAmps={:.3f}".format( ((reading * 1000) / 240) ) + print "\tAmps = {:.3f}".format( ((reading * 1000) / 240) ) diff --git a/doc.txt b/doc.txt index d9dea27..49d5bc2 100644 --- a/doc.txt +++ b/doc.txt @@ -9,6 +9,8 @@ FILE CLASSES __builtin__.object Eagle + exceptions.RuntimeError(exceptions.StandardError) + RainEagleResponseError class Eagle(__builtin__.object) | Class for talking to Rainforest Automation EAGLE (RFA-Z109) @@ -28,11 +30,13 @@ CLASSES | cloud_reset(self) | cloud_reset : Clear Cloud Configuration | + | confirm_message(self, id) + | | factory_reset(self) | Factory Reset | | get_demand_values(self, macid=None, interval='hour', frequency=None) - | Send the GET_DEMAND_VALUES command + | Send the GET_DEMAND_VALUES command | get a series of instantaneous demand values | | args: @@ -41,7 +45,11 @@ CLASSES | [Frequency] int seconds between samples | | get_device_config(self) - | get configs + | get remote management status + | + | On Success returns dict with value 'Y' or 'N' : + | 'config_ssh_enabled': 'Y' + | 'config_vpn_enabled': 'Y' | | get_device_data(self, macid=None) | Send the GET_DEVICE_DATA command to get a data dump @@ -53,9 +61,57 @@ CLASSES | args: | MacId 16 hex digits, MAC addr of EAGLE ZigBee radio | + | get_gateway_info(self) + | gets network status + | + | On Success returns dict with the values (example): + | 'gateway_cloud_id': '00:09:69' + | 'gateway_internet_status': 'connected' + | 'gateway_ip_addr': '10.11.12.13' + | 'gateway_mac_id': 'D8:D5:B9:00:90:24' + | + | get_historical_data_alt(self, period='day') + | get a series of summation values over an interval of time + | ( http command api ) + | + | args: + | period day|week|month|year + | + | On Success returns dict with the values (example): + | 'data_period' 'day', + | 'data_size' '14', + | 'timestamp[0]' '1394422200', + | 'timestamp[1]' '1394425800', + | 'timestamp[2]' '1394429400', + | 'timestamp[3]' '1394433000', + | 'timestamp[4]' '1394436600', + | 'timestamp[5]' '1394440200', + | 'timestamp[6]' '1394443800', + | 'timestamp[7]' '1394447400', + | 'timestamp[8]' '1394451000', + | 'timestamp[9]' '1394454600', + | 'timestamp[10]' '1394458200', + | 'timestamp[11]' '1394461800', + | 'timestamp[12]' '1394465400', + | 'timestamp[13]' '1394469000', + | 'value[0]' '0.429', + | 'value[1]' '0.426', + | 'value[2]' '0.422', + | 'value[3]' '0.627', + | 'value[4]' '0.735', + | 'value[5]' '0.193', + | 'value[6]' '0.026', + | 'value[7]' '-0.985', + | 'value[8]' '-1.491', + | 'value[9]' '-2.196'} + | 'value[11]' '-1.868', + | 'value[12]' '-1.330', + | 'value[13]' '-0.870', + | | 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 + | ( socket command api ) | | args: | MacId 16 hex digits, MAC addr of EAGLE ZigBee radio @@ -70,14 +126,38 @@ CLASSES | args: | MacId 16 hex digits, MAC addr of EAGLE ZigBee radio | + | get_message(self) + | On Success returns dict with the values (example): + | "meter_status" : "Connected" + | "message_timestamp" : "946684800" + | "message_text" : "" + | "message_confirmed" : "N" + | "message_confirm_required" : "N" + | "message_id" : "0" + | "message_queue" : "active" + | "message_priority" : "" + | "message_read" : "Y" + | | get_price(self) | get price for kWh + | + | On Success returns (example): + | price': '0.1300', + | price_label': 'Set by User' or '--' + | price_timestamp': '1394524458', + | price_units': '$'} + | + | returns empty dict on Error + | + | get_remote_management(self) | | get_setting_data(self) - | get settings + | get settings data + | + | returns empty dict on Error | | get_summation_values(self, macid=None, interval='day') - | Send the GET_SUMMATION_VALUES command + | Send the GET_SUMMATION_VALUES command | get a series of net summation values | | args: @@ -85,16 +165,62 @@ CLASSES | Interval day | week | month | year | | get_time_source(self, macid=None) - | get time source for device - | retrrns value "meter" or "nternet" + | get time source for device + | + | On Success returns dict with value 'internet' or 'meter' : + | 'time_source': 'internet'} | | get_timezone(self) | get current timezone configuration + | + | On Success returns dict with the value : + | 'timezone_localTime': '1394527011' + | 'timezone_olsonName': 'UTC/GMT', + | 'timezone_status': '2' + | 'timezone_utcOffset': 'UTC' + | 'timezone_utcTime': '1394527011' + | 'timezone_status': 'success' + | + | get_uploader() + | gets current uploaders config + | + | On Success returns dict with the values (example): + | "uploader_timestamp" : "1394503703" + | "uploader_provider" : "bidgely" + | "uploader_protocol" : "https" + | "uploader_hostname" : "api.bidgely.com" + | "uploader_url" : "/v1/users/44441b47-1b9a-4a65-8e8c-0efefe05bb88/homes/1/gateways/1" + | "uploader_port" : "0" + | "uploader_auth_code" : "44441b47-1b9a-4a65-8e8c-0efefe05bb88" + | "uploader_email" : "" + | "uploader_user_id" : "" + | "uploader_password" : "" + | "uploader_enabled" : "Y" + | + | See also set_cloud() to set current uploader cloud config + | + | get_uploaders(self) + | gets list of uploaders for Web UI + | + | On Success returns dict with the values (example): + | 'uploader[0]': 'none' + | 'uploader[1]': 'bidgely' + | 'uploader_name[0]': 'None' + | 'uploader_name[1]': 'Bidgely Inc.' + | + | get_usage_data(self) | | list_devices(self) | - | set_cloud(self, url) + | set_cloud(self, url, authcode='', email='') | set cloud Url + | + | args: + | url Url for uploader + | authcode + | email + | + | See also get_uploader() to retrieve current uploader cloud config | | set_fast_poll(self, macid=None, frequency='0x04', duration='0xFF') | Send the SET_FAST_POLL command @@ -105,21 +231,34 @@ CLASSES | Frequency 0x01 - 0xFF Freq to poll meter, in seconds | Duration 0x00 - 0x0F Duration of fast poll mode, in minutes (max 15) | + | set_message_read(self) + | On Success returns dict with the values : + | 'remote_management_status' : 'success' + | | set_price(self, price) - | Set price manualy + | Set price manualy | | args: | price Price/kWh + | + | On Success returns dict with value : + | 'set_price_status': 'success' | | set_price_auto(self) | Set Price from Meter + | + | On Success returns dict with value : + | 'set_price_status': 'success' | - | set_remote_management(self, macid=None, status=None) + | set_remote_management(self, macid=None, status='on') | set_remote_management | enabling ssh & vpn | | args: - | status yes|no + | status on|off + | + | On Success returns dict with value : + | 'remote_management_status': 'success' | | set_time_source(self, macid=None, source=None) | set_time_source @@ -127,6 +266,12 @@ CLASSES | | args: | source meter|internet + | + | On Success returns dict with value : + | 'set_time_source_status': u'success' + | + | On Error returns dict with value : + | 'set_time_source_status': 'invalid source name' | | ---------------------------------------------------------------------- | Data descriptors defined here: @@ -136,9 +281,79 @@ CLASSES | | __weakref__ | list of weak references to the object (if defined) + + class RainEagleResponseError(exceptions.RuntimeError) + | General exception for responce errors + | from Rainforest Automation EAGLE (RFA-Z109) + | + | Method resolution order: + | RainEagleResponseError + | exceptions.RuntimeError + | exceptions.StandardError + | exceptions.Exception + | exceptions.BaseException + | __builtin__.object + | + | Data descriptors defined here: + | + | __weakref__ + | list of weak references to the object (if defined) + | + | ---------------------------------------------------------------------- + | Methods inherited from exceptions.RuntimeError: + | + | __init__(...) + | x.__init__(...) initializes x; see help(type(x)) for signature + | + | ---------------------------------------------------------------------- + | Data and other attributes inherited from exceptions.RuntimeError: + | + | __new__ = + | T.__new__(S, ...) -> a new object with type S, a subtype of T + | + | ---------------------------------------------------------------------- + | Methods inherited from exceptions.BaseException: + | + | __delattr__(...) + | x.__delattr__('name') <==> del x.name + | + | __getattribute__(...) + | x.__getattribute__('name') <==> x.name + | + | __getitem__(...) + | x.__getitem__(y) <==> x[y] + | + | __getslice__(...) + | x.__getslice__(i, j) <==> x[i:j] + | + | Use of negative indices is not supported. + | + | __reduce__(...) + | + | __repr__(...) + | x.__repr__() <==> repr(x) + | + | __setattr__(...) + | x.__setattr__('name', value) <==> x.name = value + | + | __setstate__(...) + | + | __str__(...) + | x.__str__() <==> str(x) + | + | __unicode__(...) + | + | ---------------------------------------------------------------------- + | Data descriptors inherited from exceptions.BaseException: + | + | __dict__ + | + | args + | + | message DATA - __all__ = ['Eagle', 'to_epoch_1970, to_epoch_2000'] + __all__ = ['Eagle', 'RainEagleResponseError', 'to_epoch_1970, to_epoch... __author__ = 'Peter Shipley ' __copyright__ = 'Copyright (C) 2014 Peter Shipley' __license__ = 'BSD'