Browse Source

Merge branch 'dev'

main
Peter Shipley 11 years ago
parent
commit
c39c622933
5 changed files with 207 additions and 310 deletions
  1. +99
    -111
      RainEagle/EagleClass.py
  2. +0
    -124
      bin/get_meter_status.py
  3. +105
    -0
      bin/meter_status.py
  4. +0
    -72
      doc.txt
  5. +3
    -3
      setup.py

+ 99
- 111
RainEagle/EagleClass.py View File

@@ -1,5 +1,4 @@


__author__ = 'Peter Shipley <peter.shipley@gmail.com>'
__copyright__ = "Copyright (C) 2014 Peter Shipley"
__license__ = "BSD"
@@ -20,12 +19,11 @@ from warnings import warn
from pprint import pprint


api_arg_format = {

}
# api_arg_format = { }

__all__ = ['Eagle', 'RainEagleResponseError', 'to_epoch_1970, to_epoch_2000']


class RainEagleResponseError(RuntimeError):
"""General exception for responce errors
from Rainforest Automation EAGLE (RFA-Z109)
@@ -33,13 +31,12 @@ class RainEagleResponseError(RuntimeError):
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"
"""
if isinstance(t, time.struct_time ) :
if isinstance(t, time.struct_time) :
t = time.mktime(t)
return t - 946684800

@@ -49,7 +46,7 @@ def to_epoch_1970(t) :
offset in seconds from "Jan 1 00:00:00 2000"
to unix's epoch of 1970
"""
if isinstance(t, (int, long, float) ) :
if isinstance(t, (int, long, float)) :
return t + 946684800
if isinstance(t, str) and t.startswith('0x') :
return 946684800 + int(t, 16)
@@ -61,7 +58,7 @@ def _et2d(et) :
converts an ETree to a Dict Tree
lists are created for duplicate tag

if there are multiple XML of the name name
if there are multiple XML of the same name
an list array is used
attrib tags are converted to "tag_name" + "attrib_name"

@@ -98,7 +95,7 @@ def _et2d(et) :
return d


def twos_comp(val, bits=32):
def _twos_comp(val, bits=32):
"""compute the 2's compliment of int value val"""
if( (val&(1<<(bits-1))) != 0 ):
val = val - (1<<bits)
@@ -121,8 +118,9 @@ def _tohex(n, width=8) :
# add two for the "0x"
width += 2

if (i > 2147483647) or ( i < -2147483648 ) :
warn("_tohex : signed int to large (" + str(n) + ")\n", RuntimeWarning, stacklevel=2)
if (i > 2147483647) or (i < -2147483648) :
warn("_tohex : signed int to large (" + str(n) + ")\n",
RuntimeWarning, stacklevel=2)

if i < 0 :
i += 0x100000000
@@ -140,6 +138,7 @@ class Eagle(object) :
addr address of device
port port on device (default 5002)
getmac connect to device at start up and get macid (default true)
timeout TCP socket timeout

Currently there is very little error handling ( if any at all )
"""
@@ -182,7 +181,7 @@ class Eagle(object) :
print "comm_responce =", comm_responce
if comm_responce is None:
raise RainEagleResponseError("list_devices : Null reply")
etree = ET.fromstring('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
if self.macid is None :
self.macid = rv['DeviceInfo']['DeviceMacId']
@@ -196,7 +195,7 @@ class Eagle(object) :
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('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

@@ -214,12 +213,12 @@ class Eagle(object) :
MacId=macid)
if comm_responce is None:
raise RainEagleResponseError("get_instantaneous_demand : Null reply")
etree = ET.fromstring('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

# 11
def get_demand_values(self, macid=None, interval="hour", frequency=None ) :
def get_demand_values(self, macid=None, interval="hour", frequency=None) :
""" Send the GET_DEMAND_VALUES command
get a series of instantaneous demand values

@@ -238,7 +237,7 @@ class Eagle(object) :
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('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

@@ -256,10 +255,10 @@ class Eagle(object) :
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 )
MacId=macid, Interval=interval)
if comm_responce is None:
raise RainEagleResponseError("get_summation_values : Null reply")
etree = ET.fromstring('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

@@ -282,7 +281,7 @@ class Eagle(object) :
MacId=macid, Frequency=frequency, Duration=duration)
if comm_responce is None:
raise RainEagleResponseError("set_fast_poll : Null reply")
etree = ET.fromstring('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

@@ -299,13 +298,13 @@ class Eagle(object) :
comm_responce = self._send_soc_comm("get_fast_poll_status", MacId=macid)
if comm_responce is None:
return None
etree = ET.fromstring('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

return rv

# 17
def get_history_data(self, macid=None, starttime="0x00000000", endtime=None, frequency=None ) :
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
( socket command api )
@@ -327,7 +326,7 @@ class Eagle(object) :
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('<S>' + comm_responce + '</S>' )
etree = ET.fromstring('<S>' + comm_responce + '</S>')
rv = _et2d(etree)
return rv

@@ -363,13 +362,13 @@ class Eagle(object) :
"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 :
@@ -406,27 +405,27 @@ class Eagle(object) :
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'}
'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")
@@ -434,7 +433,7 @@ class Eagle(object) :


def get_historical_data_alt(self, period="day") :
"""
"""
get a series of summation values over an interval of time
( http command api )

@@ -442,49 +441,43 @@ class Eagle(object) :
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'] :
'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 data
@@ -528,13 +521,12 @@ class Eagle(object) :

On Success returns dict with the value :
'timezone_localTime': '1394527011'
'timezone_olsonName': 'UTC/GMT',
'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)
@@ -544,7 +536,7 @@ class Eagle(object) :
get time source for device

On Success returns dict with value 'internet' or 'meter' :
'time_source': 'internet'}
'time_source': 'internet'
"""
comm_responce = self._send_http_comm("get_time_source")
return json.loads(comm_responce)
@@ -592,10 +584,10 @@ class Eagle(object) :
get price for kWh

On Success returns (example):
price': '0.1300',
price': '0.1300'
price_label': 'Set by User' or '--'
price_timestamp': '1394524458',
price_units': '$'}
price_timestamp': '1394524458'
price_units': '$'

returns empty dict on Error
"""
@@ -616,21 +608,22 @@ class Eagle(object) :
if isinstance(price, str) and price.startswith('$') :
price = float(price.lstrip('$'))

if not isinstance(price, (int, long, float) ) :
if not isinstance(price, (int, long, float)) :
raise ValueError("set_price price arg must me a int, long or float")
if ( price <= 0 ):
if (price <= 0):
raise ValueError("set_price price arg greater then 0")

trailing_digits = 0
multiplier = 1
while (((price * multiplier) != (floor(price * multiplier))) and (trailing_digits < 7) ) :
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 )
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)
comm_responce = self._send_http_comm("set_price",
Price=price_adj, TrailingDigits=tdigits)
return json.loads(comm_responce)


@@ -642,8 +635,8 @@ class Eagle(object) :
'set_price_status': 'success'
"""
comm_responce = self._send_http_comm("set_price",
Price="0xFFFFFFFF",
TrailingDigits="0x00")
Price="0xFFFFFFFF",
TrailingDigits="0x00")
return json.loads(comm_responce)

# def set_multiplier_divisor(self, multiplier=1, divisor=1) :
@@ -671,7 +664,6 @@ class Eagle(object) :
# return json.loads(comm_responce)



def cloud_reset(self) :
"""
cloud_reset : Clear Cloud Configuration
@@ -722,22 +714,19 @@ class Eagle(object) :
password = ""

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)
Provider="manual",
Protocol=protocol, HostName=hostname,
Url=url, Port=port,
AuthCode=authcode, Email=email,
UserId=userid, Password=password)

return json.loads(comm_responce)





# 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 :
@@ -747,7 +736,6 @@ class Eagle(object) :
except IOError :
pass


def _send_http_comm(self, cmd, **kwargs):

print "\n\n_send_http_comm : ", cmd


+ 0
- 124
bin/get_meter_status.py View File

@@ -1,124 +0,0 @@
#!/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 twos_comp(val, bits=32):
"""compute the 2's compliment of int value val"""
if( (val&(1<<(bits-1))) != 0 ):
val = val - (1<<bits)
return val

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\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 = twos_comp(int(idemand['Demand'], 16))
demand=int(idemand['Demand'], 16)
if demand > 0x7FFFFFFF:
demand -= 0x100000000

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



+ 105
- 0
bin/meter_status.py View File

@@ -0,0 +1,105 @@
#!/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

debug = 0

def main() :
eg = Eagle( debug=debug , addr="10.1.1.39")
# timeout=45,

r = eg.get_device_data()

print_instantdemand( r['InstantaneousDemand'])
print

print_currentsummation(r['CurrentSummation'])
print

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<<bits)
return val

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 "{0:s} : ".format(time.asctime(time.localtime(time_stamp)))
print "\tReceived = {0:{width}.3f} Kw".format(reading_received, width=10)
print "\tDelivered = {0:{width}.3f} Kw".format(reading_delivered, width=10)
print "\t\t{0:{width}.3f} Kw".format( (reading_delivered - reading_received), width=14)


# 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 = twos_comp(int(idemand['Demand'], 16))

demand = int(idemand['Demand'], 16)

if demand > 0x7FFFFFFF:
demand -= 0x100000000

if multiplier == 0 :
multiplier = 1

if divisor == 0 :
divisor = 1

reading = (demand * multiplier) / float (divisor )

print "{0:s} : ".format(time.asctime(time.localtime(time_stamp)))
print "\tDemand = {0:{width}.3f} Kw".format(reading, width=10)
print "\tAmps = {0:{width}.3f}".format( ((reading * 1000) / 240), width=10 )


#
if __name__ == "__main__":
# import __main__
# print(__main__.__file__)
# print("syntax ok")
main()
exit(0)



+ 0
- 72
doc.txt View File

@@ -273,84 +273,12 @@ CLASSES
| On Error returns dict with value :
| 'set_time_source_status': 'invalid source name'
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __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__ = <built-in method __new__ of type object>
| 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', 'RainEagleResponseError', 'to_epoch_1970, to_epoch...


+ 3
- 3
setup.py View File

@@ -33,11 +33,11 @@ setup(
author='Peter Shipley',
author_email='Peter.Shipley@gmail.com',
packages=find_packages(),
scripts=[ 'binbin/get_meter_status.py', 'bin/plot_power.py' ]
data_files=[
scripts=[ 'bin/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']) ],
# ('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.',


Loading…
Cancel
Save