Browse Source

Merge branch 'dev'

Conflicts:
	RainEagle/EagleClass.py
main
Peter Shipley 11 years ago
parent
commit
d94afe3303
9 changed files with 1574 additions and 221 deletions
  1. +2
    -0
      .gitignore
  2. +19
    -2
      README.md
  3. +750
    -216
      RainEagle/EagleClass.py
  4. +4
    -3
      RainEagle/__init__.py
  5. +125
    -0
      Tests/get_hist.py
  6. +124
    -0
      bin/get_meter_status.py
  7. +136
    -0
      bin/plot_power.py
  8. +364
    -0
      doc.txt
  9. +50
    -0
      setup.py

+ 2
- 0
.gitignore View File

@@ -7,6 +7,8 @@ Sav/*
Bak/*
*bak

WWW/*

# C extensions
*.so



+ 19
- 2
README.md View File

@@ -18,18 +18,35 @@ API Call list :

```python

# Socket API based commands
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')


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

```

Calls return dictionarys containing data results
API Calls return dictionarys containing data results,
raises exception or returns None if error

## External Documentation



+ 750
- 216
RainEagle/EagleClass.py
File diff suppressed because it is too large
View File


+ 4
- 3
RainEagle/__init__.py View File

@@ -10,14 +10,15 @@ if sys.hexversion < 0x20703f0 :
__revision__ = "$Id: 20140301 $"
__version__ = '0.1.20140301'
__author__ = 'Peter Shipley <peter.shipley@gmail.com>'
__copyright__ = "Copyright (C) 2014 Peter Shipley"
__copyright__ = "Copyright (C) 2014 Peter Shipley"
__license__ = "BSD"


from EagleClass import Eagle
import EagleClass
from EagleClass import Eagle, RainEagleResponseError, to_epoch_1970, to_epoch_2000
#from RainEagle.EagleClass import Eagle

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





+ 125
- 0
Tests/get_hist.py View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python

"""
based on TalkToEagle.py
"""


import socket
import sys
import time
import xml.etree.ElementTree as ET

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)
print s
time.sleep(1)

sendstr = "<LocalCommand>\n<Name>list_devices</Name>\n</LocalCommand>\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 = "<LocalCommand>\n<Name>get_history_data</Name>\n<MacId>{0}</MacId>\n<StartTime>0x00000000</StartTime>\n</LocalCommand>\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"

j=0
buf_list = []
while 1:
buf = s.recv(1000)
if not buf:
break
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)

+ 124
- 0
bin/get_meter_status.py View File

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



+ 136
- 0
bin/plot_power.py View File

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

+ 364
- 0
doc.txt View File

@@ -0,0 +1,364 @@
Help on module EagleClass:

NAME
EagleClass

FILE
RainEagle/EagleClass.py

CLASSES
__builtin__.object
Eagle
exceptions.RuntimeError(exceptions.StandardError)
RainEagleResponseError
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
|
| 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
| 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 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
|
| 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_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
| 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_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 data
|
| returns empty dict on Error
|
| 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
|
| 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, 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
| 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_message_read(self)
| On Success returns dict with the values :
| 'remote_management_status' : 'success'
|
| set_price(self, price)
| 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='on')
| set_remote_management
| enabling ssh & vpn
|
| args:
| status on|off
|
| On Success returns dict with value :
| 'remote_management_status': 'success'
|
| set_time_source(self, macid=None, source=None)
| set_time_source
| set time source
|
| 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:
|
| __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...
__author__ = 'Peter Shipley <peter.shipley@gmail.com>'
__copyright__ = 'Copyright (C) 2014 Peter Shipley'
__license__ = 'BSD'

AUTHOR
Peter Shipley <peter.shipley@gmail.com>



+ 50
- 0
setup.py View File

@@ -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 }
)





Loading…
Cancel
Save