view service/powerEagle/reader.py @ 1523:0da337780f22

dep updates; graph url renames; and other build updates Ignore-this: 4603ef3d8db650a13e543dad8580ade8 darcs-hash:0840491cd6a4e4966f47a2abca0ed30ec434022a
author drewp <drewp@bigasterisk.com>
date Wed, 05 Feb 2020 00:23:06 -0800
parents 013af6808aca
children b87b6e9cedb2
line wrap: on
line source

#!bin/python
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
import cyclone.web

from standardservice.logsetup import log, verboseLogging

from private_config import deviceIp, cloudId, installId, macId, periodSec

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, 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(
                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>''').encode('ascii'),
                timeout=10)
            ret = json.loads(resp.body)
            log.debug(ret)
            if ret['demand_units'] != 'kW':
                raise ValueError
            if ret['summation_units'] != 'kWh':
                raise ValueError
            pts = [
                dict(measurement='housePowerW',
                     fields=dict(value=float(ret['demand']) * 1000),
                     tags=dict(house='berkeley'),
                     time=int(startTime))]
            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)))
            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),
                ))
                   
            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()

        now = time.time()
        goal = startTime + periodSec - .2
        reactor.callLater(max(1, goal - now), self.poll)


if __name__ == '__main__':
    arg = docopt("""
    Usage: reader.py [options]

    -v           Verbose
    --port PORT  Serve on port [default: 10016].
    """)
    verboseLogging(arg['-v'])

    influx = InfluxDBClient('bang', 9060, 'root', 'root', 'main')
    p = Poller(influx)

    reactor.listenTCP(
        int(arg['--port']),
        cyclone.web.Application(
            [
                (r'/stats/(.*)', StatsHandler, {'serverName': 'powerEagle'}),
                (r"/graph/power", CycloneGraphHandler, {'masterGraph': masterGraph}),
                (r"/graph/power/events", CycloneGraphEventsHandler, {'masterGraph': masterGraph}),
            ],
        ))
    reactor.run()