changeset 532:71aa55cd8433

update powereagle to py3, be a server with /stats/, save 'price' field Ignore-this: 1b738295b503fb7409311ddd637b7a18
author drewp@bigasterisk.com
date Tue, 23 Apr 2019 03:57:55 -0700
parents f372e9d358d2
children bb6b4988dd75
files service/powerEagle/Dockerfile service/powerEagle/reader.py service/powerEagle/requirements.txt service/powerEagle/tasks.py
diffstat 4 files changed, 105 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/powerEagle/Dockerfile	Tue Apr 23 03:57:55 2019 -0700
@@ -0,0 +1,15 @@
+FROM bang6:5000/base_x86
+
+WORKDIR /opt
+
+COPY requirements.txt ./
+
+RUN pip3 install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt
+# not sure why this doesn't work from inside requirements.txt
+RUN pip3 install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3'
+
+COPY *.py *.html ./
+
+EXPOSE 10016
+
+CMD [ "python3", "reader.py" ]
--- a/service/powerEagle/reader.py	Tue Apr 23 02:56:07 2019 -0700
+++ b/service/powerEagle/reader.py	Tue Apr 23 03:57:55 2019 -0700
@@ -1,42 +1,53 @@
 #!bin/python
-import json, logging, time, os
-import sys
-sys.path.append("/my/proj/homeauto/lib")
-from logsetup import log
+import json, time, os, binascii, traceback
+
+from cyclone.httpclient import fetch
+from docopt import docopt
+from greplin import scales
+from greplin.scales.cyclonehandler import StatsHandler
+from influxdb import InfluxDBClient
+from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks
-from twisted.internet import reactor
-from cyclone.httpclient import fetch
-from influxdb import InfluxDBClient
+import cyclone.web
+
+from standardservice.logsetup import log, verboseLogging
 
 from private_config import deviceIp, cloudId, installId, macId, periodSec
 
-auth = (cloudId + ':' + installId).encode('base64').strip()
-influx = InfluxDBClient('bang', 9060, 'root', 'root', 'main')
+STATS = scales.collection('/root',
+                          scales.PmfStat('poll'),
+                          )
+
+authPlain = cloudId + ':' + installId
+auth = binascii.b2a_base64(authPlain.encode('ascii')).strip(b'=\n')
 
 class Poller(object):
-    def __init__(self, carbon):
-        self.carbon = carbon
+    def __init__(self, influx):
+        self.influx = influx
         reactor.callLater(0, self.poll)
 
+    @STATS.poll.time()
     @inlineCallbacks
     def poll(self):
         ret = None
         startTime = time.time()
         try:
+            url = (f'http://{deviceIp}/cgi-bin/cgi_manager').encode('ascii')
             resp = yield fetch(
-                'http://{deviceIp}/cgi-bin/cgi_manager'.format(deviceIp=deviceIp),
-                method='POST',
-                headers={'Authorization': ['Basic %s' % auth]},
-                postdata='''<LocalCommand>
+                url,
+                method=b'POST',
+                headers={b'Authorization': [b'Basic %s' % auth]},
+                postdata=(f'''<LocalCommand>
                               <Name>get_usage_data</Name>
                               <MacId>0x{macId}</MacId>
                             </LocalCommand>
                             <LocalCommand>
                               <Name>get_price_blocks</Name>
                               <MacId>0x{macId}</MacId>
-                            </LocalCommand>'''.format(macId=macId),
+                            </LocalCommand>''').encode('ascii'),
                 timeout=10)
             ret = json.loads(resp.body)
+            log.debug(ret)
             if ret['demand_units'] != 'kW':
                 raise ValueError
             if ret['summation_units'] != 'kWh':
@@ -49,12 +60,21 @@
             sd = float(ret['summation_delivered'])
             if sd > 0: # Sometimes nan
                 pts.append(dict(measurement='housePowerSumDeliveredKwh',
-                     fields=dict(value=float()),
-                     tags=dict(house='berkeley'),
-                     time=int(startTime)))
+                                fields=dict(value=float()),
+                                tags=dict(house='berkeley'),
+                                time=int(startTime)))
+            if 'price' in ret:
+                pts.append(dict(
+                    measurement='price',
+                    fields=dict(price=float(ret['price']),
+                                price_units=float(ret['price_units'])),
+                    tags=dict(house='berkeley'),
+                    time=int(startTime),
+                ))
                    
-            influx.write_points(pts, time_precision='s')
+            self.influx.write_points(pts, time_precision='s')
         except Exception as e:
+            traceback.print_exc()
             log.error("failed: %r", e)
             log.error(repr(ret))
             os.abort()
@@ -64,8 +84,23 @@
         reactor.callLater(max(1, goal - now), self.poll)
 
 
-log.setLevel(logging.INFO)
-influx = InfluxDBClient('bang', 9060, 'root', 'root', 'main')
+if __name__ == '__main__':
+    arg = docopt("""
+    Usage: reader.py [options]
+
+    -v           Verbose
+    --port PORT  Serve on port [default: 10016].
+    """)
+    verboseLogging(arg['-v'])
 
-p = Poller(influx)
-reactor.run()
+    influx = InfluxDBClient('bang', 9060, 'root', 'root', 'main')
+    p = Poller(influx)
+
+    reactor.listenTCP(
+        int(arg['--port']),
+        cyclone.web.Application(
+            [
+                (r'/stats/(.*)', StatsHandler, {'serverName': 'powerEagle'}),
+            ],
+        ))
+    reactor.run()
--- a/service/powerEagle/requirements.txt	Tue Apr 23 02:56:07 2019 -0700
+++ b/service/powerEagle/requirements.txt	Tue Apr 23 03:57:55 2019 -0700
@@ -1,4 +1,5 @@
-cyclone==1.1
-service-identity==14.0.0
-Twisted==15.5.0
+cyclone
 influxdb==3.0.0
+standardservice==0.3.0
+git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales
+https://github.com/drewp/cyclone/archive/python3.zip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/powerEagle/tasks.py	Tue Apr 23 03:57:55 2019 -0700
@@ -0,0 +1,27 @@
+from invoke import task
+
+JOB = 'powerEagle'
+PORT = 10016
+TAG = f'bang6:5000/{JOB.lower()}_x86:latest'
+
+
+@task
+def build_image(ctx):
+    ctx.run(f'docker build --network=host -t {TAG} .')
+
+@task(pre=[build_image])
+def push_image(ctx):
+    ctx.run(f'docker push {TAG}')
+
+@task(pre=[build_image])
+def shell(ctx):
+    ctx.run(f'docker run --rm -it --cap-add SYS_PTRACE --net=host {TAG} /bin/bash', pty=True)
+
+@task(pre=[build_image])
+def local_run(ctx):
+    ctx.run(f'docker run --rm -it -p {PORT}:{PORT} --net=host {TAG} python3 reader.py -v', pty=True)
+
+@task(pre=[push_image])
+def redeploy(ctx):
+    ctx.run(f'sudo /my/proj/ansible/playbook -l bang -t {JOB}')
+    ctx.run(f'supervisorctl -s http://bang:9001/ restart {JOB}_{PORT}')