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) +