| @@ -9,13 +9,32 @@ import sys | |||||
| import os | import os | ||||
| import time | import time | ||||
| import xml.etree.ElementTree as ET | import xml.etree.ElementTree as ET | ||||
| import urllib | |||||
| import urllib2 | |||||
| from math import floor | |||||
| from urlparse import urlparse | |||||
| import json | |||||
| from pprint import pprint | 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) : | def _et2d(et) : | ||||
| @@ -60,6 +79,19 @@ def _et2d(et) : | |||||
| d[child.tag] = child.text | d[child.tag] = child.text | ||||
| return d | 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): | def list_devices(self): | ||||
| comm_responce = self._send_comm("list_devices") | |||||
| comm_responce = self._send_soc_comm("list_devices") | |||||
| if self.debug : | if self.debug : | ||||
| print "comm_responce =", comm_responce | print "comm_responce =", comm_responce | ||||
| if comm_responce == None: | if comm_responce == None: | ||||
| @@ -125,7 +157,7 @@ class Eagle(object) : | |||||
| """ Send the GET_DEVICE_DATA command to get a data dump """ | """ Send the GET_DEVICE_DATA command to get a data dump """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | 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: | if comm_responce == None: | ||||
| return None | return None | ||||
| etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | ||||
| @@ -142,7 +174,7 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | macid = self.macid | ||||
| comm_responce = self._send_comm("get_instantaneous_demand", | |||||
| comm_responce = self._send_soc_comm("get_instantaneous_demand", | |||||
| MacId=macid) | MacId=macid) | ||||
| if comm_responce == None: | if comm_responce == None: | ||||
| return None | return None | ||||
| @@ -162,10 +194,12 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | 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} | kwargs = {"MacId": macid, "Interval": interval} | ||||
| if frequency : | 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: | if comm_responce == None: | ||||
| return None | return None | ||||
| etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | ||||
| @@ -183,7 +217,9 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | 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 ) | MacId=macid, Interval=interval ) | ||||
| if comm_responce == None: | if comm_responce == None: | ||||
| return None | return None | ||||
| @@ -203,12 +239,10 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | 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) | MacId=macid, Frequency=frequency, Duration=duration) | ||||
| if comm_responce == None: | if comm_responce == None: | ||||
| return None | return None | ||||
| @@ -226,7 +260,7 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | 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: | if comm_responce == None: | ||||
| return None | return None | ||||
| etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | ||||
| @@ -247,12 +281,13 @@ class Eagle(object) : | |||||
| """ | """ | ||||
| if macid == None : | if macid == None : | ||||
| macid = self.macid | macid = self.macid | ||||
| kwargs = {"MacId": macid, "StartTime": starttime} | |||||
| kwargs = {"MacId": macid,} | |||||
| kwargs["StartTime"] = _tohex(starttime, 10) | |||||
| if endtime : | if endtime : | ||||
| kwargs["EndTime"] = endtime | |||||
| kwargs["EndTime"] = _tohex(endtime, 10) | |||||
| if frequency : | 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 : | if comm_responce == None : | ||||
| return None | return None | ||||
| etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | etree = ET.fromstring('<S>' + comm_responce + '</S>' ) | ||||
| @@ -260,7 +295,165 @@ class Eagle(object) : | |||||
| return rv | 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) : | def _connect(self) : | ||||
| self.soc = socket.create_connection( (self.addr, self.port), 10) | self.soc = socket.create_connection( (self.addr, self.port), 10) | ||||
| @@ -274,7 +467,30 @@ class Eagle(object) : | |||||
| pass | pass | ||||
| def _send_comm(self, cmd, **kwargs): | |||||
| def _send_http_comm(self, cmd, **kwargs): | |||||
| print "\n\n_send_http_comm : ", cmd | |||||
| commstr = "<LocalCommand>\n" | |||||
| commstr += "<Name>{0!s}</Name>\n".format(cmd) | |||||
| commstr += "<MacId>{0!s}</MacId>\n".format(self.macid) | |||||
| for k, v in kwargs.items() : | |||||
| commstr += "<{0}>{1!s}</{0}>\n".format(k, v) | |||||
| commstr += "</LocalCommand>\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" : | if cmd == "set_fast_poll" : | ||||
| command_tag = "RavenCommand" | command_tag = "RavenCommand" | ||||
| @@ -288,6 +504,7 @@ class Eagle(object) : | |||||
| commstr += "<{0}>{1!s}</{0}>\n".format(k, v) | commstr += "<{0}>{1!s}</{0}>\n".format(k, v) | ||||
| commstr += "</{0}>\n".format(command_tag) | commstr += "</{0}>\n".format(command_tag) | ||||
| replystr = "" | replystr = "" | ||||
| # buf_list = [] | |||||
| try: | try: | ||||
| self._connect() | self._connect() | ||||
| @@ -305,6 +522,8 @@ class Eagle(object) : | |||||
| if not buf: | if not buf: | ||||
| break | break | ||||
| replystr += buf | replystr += buf | ||||
| #buf_list.append(buf) | |||||
| # replystr = ''.join(buf_list) | |||||
| except Exception: | except Exception: | ||||
| print("Unexpected error:", sys.exc_info()[0]) | print("Unexpected error:", sys.exc_info()[0]) | ||||
| @@ -313,18 +532,9 @@ class Eagle(object) : | |||||
| finally: | finally: | ||||
| self._disconnect() | self._disconnect() | ||||
| if self.debug > 1 : | if self.debug > 1 : | ||||
| print "_send_comm replystr :\n", replystr | |||||
| print "_send_soc_comm replystr :\n", replystr | |||||
| return 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 | # Do nothing | ||||
| # (syntax check) | # (syntax check) | ||||