Mercurial > code > home > repos > homeauto
annotate service/powerEagle/reader.py @ 1723:bacb13c10c7a
lookup ip in a roundabout way
author | drewp@bigasterisk.com |
---|---|
date | Fri, 16 Jun 2023 22:23:33 -0700 |
parents | fb082013fa24 |
children | d012c53c5ae8 |
rev | line source |
---|---|
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
1 import binascii |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
2 import json |
1714 | 3 import logging |
1723 | 4 from pprint import pprint |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
5 from typing import Dict |
532
71aa55cd8433
update powereagle to py3, be a server with /stats/, save 'price' field
drewp@bigasterisk.com
parents:
416
diff
changeset
|
6 |
1714 | 7 import aiohttp |
1719
fb082013fa24
port to use my background_loop lib. various other dep updates.
drewp@bigasterisk.com
parents:
1714
diff
changeset
|
8 import background_loop |
1714 | 9 from patchablegraph import PatchableGraph |
10 from patchablegraph.handler import GraphEvents, StaticGraph | |
1719
fb082013fa24
port to use my background_loop lib. various other dep updates.
drewp@bigasterisk.com
parents:
1714
diff
changeset
|
11 from prometheus_client import Gauge |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
12 from rdflib import Literal, Namespace |
1714 | 13 from starlette.applications import Starlette |
14 from starlette.routing import Route | |
15 from starlette_exporter import PrometheusMiddleware, handle_metrics | |
532
71aa55cd8433
update powereagle to py3, be a server with /stats/, save 'price' field
drewp@bigasterisk.com
parents:
416
diff
changeset
|
16 |
1723 | 17 from private_config import cloudId, deviceMac, installId, macId, periodSec |
1714 | 18 |
734 | 19 ROOM = Namespace("http://projects.bigasterisk.com/room/") |
211 | 20 |
1714 | 21 logging.basicConfig(level=logging.INFO) |
22 log = logging.getLogger() | |
23 | |
532
71aa55cd8433
update powereagle to py3, be a server with /stats/, save 'price' field
drewp@bigasterisk.com
parents:
416
diff
changeset
|
24 authPlain = cloudId + ':' + installId |
71aa55cd8433
update powereagle to py3, be a server with /stats/, save 'price' field
drewp@bigasterisk.com
parents:
416
diff
changeset
|
25 auth = binascii.b2a_base64(authPlain.encode('ascii')).strip(b'=\n') |
211 | 26 |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
27 |
1723 | 28 async def getOneSseEvent(url): |
29 async with aiohttp.ClientSession() as session: | |
30 async with session.get(url) as response: | |
31 line1 = None | |
32 async for line_in_bytes in response.content: | |
33 if line1 is None: | |
34 line1 = line_in_bytes | |
35 else: | |
36 return line1, line_in_bytes | |
37 raise ValueError | |
38 | |
39 | |
40 def getJsonldValue(g, k): | |
41 return g[k][0]['@value'] | |
42 | |
43 | |
44 def ipLookup(collectorJsonLd, mac): | |
45 for add in collectorJsonLd['patch']['adds']: | |
46 for g in add['@graph']: | |
47 try: | |
48 if getJsonldValue(g, 'http://projects.bigasterisk.com/room/macAddress') == mac: | |
49 return getJsonldValue(g, 'http://projects.bigasterisk.com/room/assignedIp') | |
50 except (KeyError, ValueError): | |
51 pass | |
52 raise ValueError | |
53 | |
54 | |
211 | 55 class Poller(object): |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
56 |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
57 def __init__(self, out: Dict[str, Gauge], graph): |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
58 self.out = out |
734 | 59 self.graph = graph |
1714 | 60 |
1719
fb082013fa24
port to use my background_loop lib. various other dep updates.
drewp@bigasterisk.com
parents:
1714
diff
changeset
|
61 async def poll(self, first_run: bool): |
1723 | 62 line1, line2 = await getOneSseEvent('http://collector.default.svc.cluster.local/collector/graph/lanscape') |
63 p = json.loads(line2.split(b'data: ', 1)[1]) | |
64 | |
65 deviceIp = ipLookup(p, deviceMac) | |
66 | |
1714 | 67 url = (f'http://{deviceIp}/cgi-bin/cgi_manager') |
68 | |
69 async with aiohttp.ClientSession() as session: | |
70 async with session.post(url, | |
71 headers={'Authorization': 'Basic %s' % auth.decode('ascii')}, | |
72 data=(f'''<LocalCommand> | |
73 <Name>get_usage_data</Name> | |
74 <MacId>0x{macId}</MacId> | |
75 </LocalCommand> | |
76 <LocalCommand> | |
77 <Name>get_price_blocks</Name> | |
78 <MacId>0x{macId}</MacId> | |
79 </LocalCommand>'''), | |
80 timeout=10) as response: | |
211 | 81 |
1714 | 82 ret = json.loads(await response.text()) |
83 log.debug(f"response body {ret}") | |
84 if ret['demand_units'] != 'kW': | |
85 raise ValueError | |
86 if ret['summation_units'] != 'kWh': | |
87 raise ValueError | |
88 | |
89 demandW = float(ret['demand']) * 1000 | |
90 self.out['w'].set(demandW) | |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
91 |
1714 | 92 sd = float(ret['summation_delivered']) |
93 if sd > 0: # Sometimes nan | |
94 self.out['kwh'].set(sd) | |
734 | 95 |
1714 | 96 if 'price' in ret: |
97 self.out['price'].set(float(ret['price'])) | |
211 | 98 |
1714 | 99 self.graph.patchObject(context=ROOM['powerEagle'], |
100 subject=ROOM['housePower'], | |
101 predicate=ROOM['instantDemandWatts'], | |
102 newObject=Literal(demandW)) | |
211 | 103 |
104 | |
1714 | 105 def main(): |
106 masterGraph = PatchableGraph() | |
311 | 107 |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
108 out = { |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
109 'w': Gauge('house_power_w', 'house power demand'), |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
110 'kwh': Gauge('house_power_kwh', 'house power sum delivered'), |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
111 'price': Gauge('house_power_price', 'house power price'), |
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
112 } |
1714 | 113 |
786
e8654a3bd1c7
update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents:
734
diff
changeset
|
114 p = Poller(out, masterGraph) |
532
71aa55cd8433
update powereagle to py3, be a server with /stats/, save 'price' field
drewp@bigasterisk.com
parents:
416
diff
changeset
|
115 |
1714 | 116 # todo: background_loop isn't trying to maintain a goal of periodSec |
1719
fb082013fa24
port to use my background_loop lib. various other dep updates.
drewp@bigasterisk.com
parents:
1714
diff
changeset
|
117 loop = background_loop.loop_forever(p.poll, periodSec) |
1714 | 118 |
119 app = Starlette(debug=True, | |
120 routes=[ | |
121 Route('/graph/power', StaticGraph(masterGraph)), | |
122 Route('/graph/power/events', GraphEvents(masterGraph)), | |
123 ]) | |
124 | |
125 app.add_middleware(PrometheusMiddleware, app_name='power_eagle') | |
126 app.add_route("/metrics", handle_metrics) | |
127 return app | |
128 | |
129 | |
130 app = main() |