view service/powerEagle/reader.py @ 712:d98c3ffe7144

new graph output for browsers, with autorefresh Ignore-this: e4ff4dbed311b238d90988a1891ef640
author drewp@bigasterisk.com
date Mon, 03 Feb 2020 23:47:23 -0800
parents 71aa55cd8433
children a93fbf0d0daa
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'}),
            ],
        ))
    reactor.run()