Mercurial > code > home > repos > homeauto
view service/powerEagle/reader.py @ 1694:73abfd4cf5d0
new html log and other refactoring as i work on the advanceTheStack problems
https://bigasterisk.com/post/inference/2021-09-27_11-11.png
author | drewp@bigasterisk.com |
---|---|
date | Mon, 27 Sep 2021 11:22:09 -0700 |
parents | e8654a3bd1c7 |
children | 4cbe3df8f48f |
line wrap: on
line source
import binascii import json import time import traceback from typing import Dict from cyclone.httpclient import fetch import cyclone.web from patchablegraph import ( CycloneGraphEventsHandler, CycloneGraphHandler, PatchableGraph, ) from prometheus_client import Counter, Gauge, Summary from prometheus_client.exposition import generate_latest from prometheus_client.registry import REGISTRY from rdflib import Literal, Namespace from standardservice.logsetup import log, verboseLogging from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks from docopt import docopt from private_config import cloudId, deviceIp, installId, macId, periodSec ROOM = Namespace("http://projects.bigasterisk.com/room/") authPlain = cloudId + ':' + installId auth = binascii.b2a_base64(authPlain.encode('ascii')).strip(b'=\n') POLL = Summary('poll', 'Time in HTTP poll requests') POLL_SUCCESSES = Counter('poll_successes', 'poll success count') POLL_ERRORS = Counter('poll_errors', 'poll error count') class Poller(object): def __init__(self, out: Dict[str, Gauge], graph): self.out = out self.graph = graph reactor.callLater(0, self.poll) @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(f"response body {ret}") if ret['demand_units'] != 'kW': raise ValueError if ret['summation_units'] != 'kWh': raise ValueError demandW = float(ret['demand']) * 1000 self.out['w'].set(demandW) sd = float(ret['summation_delivered']) if sd > 0: # Sometimes nan self.out['kwh'].set(sd) if 'price' in ret: self.out['price'].set(float(ret['price'])) self.graph.patchObject(context=ROOM['powerEagle'], subject=ROOM['housePower'], predicate=ROOM['instantDemandWatts'], newObject=Literal(demandW)) POLL_SUCCESSES.inc() except Exception as e: POLL_ERRORS.inc() traceback.print_exc() log.error("failed: %r", e) log.error(repr(ret)) now = time.time() goal = startTime + periodSec - .2 reactor.callLater(max(1, goal - now), self.poll) class Metrics(cyclone.web.RequestHandler): def get(self): self.add_header('content-type', 'text/plain') self.write(generate_latest(REGISTRY)) if __name__ == '__main__': arg = docopt(""" Usage: reader.py [options] -v Verbose --port PORT Serve on port [default: 10016]. """) verboseLogging(arg['-v']) out = { 'w': Gauge('house_power_w', 'house power demand'), 'kwh': Gauge('house_power_kwh', 'house power sum delivered'), 'price': Gauge('house_power_price', 'house power price'), } masterGraph = PatchableGraph() p = Poller(out, masterGraph) reactor.listenTCP( int(arg['--port']), cyclone.web.Application([ (r'/metrics', Metrics), (r"/graph/power", CycloneGraphHandler, { 'masterGraph': masterGraph }), (r"/graph/power/events", CycloneGraphEventsHandler, { 'masterGraph': masterGraph }), ],)) reactor.run()