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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
3 import logging
1723
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
9 from patchablegraph import PatchableGraph
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
13 from starlette.applications import Starlette
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
14 from starlette.routing import Route
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
17 from private_config import cloudId, deviceMac, installId, macId, periodSec
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
18
734
e9366f73e612 serve current usage as a graph
drewp@bigasterisk.com
parents: 723
diff changeset
19 ROOM = Namespace("http://projects.bigasterisk.com/room/")
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
20
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
21 logging.basicConfig(level=logging.INFO)
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
22 log = logging.getLogger()
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
26
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
27
1723
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
28 async def getOneSseEvent(url):
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
29 async with aiohttp.ClientSession() as session:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
30 async with session.get(url) as response:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
31 line1 = None
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
32 async for line_in_bytes in response.content:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
33 if line1 is None:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
34 line1 = line_in_bytes
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
35 else:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
36 return line1, line_in_bytes
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
37 raise ValueError
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
38
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
39
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
40 def getJsonldValue(g, k):
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
41 return g[k][0]['@value']
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
42
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
43
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
44 def ipLookup(collectorJsonLd, mac):
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
45 for add in collectorJsonLd['patch']['adds']:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
46 for g in add['@graph']:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
47 try:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
48 if getJsonldValue(g, 'http://projects.bigasterisk.com/room/macAddress') == mac:
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
49 return getJsonldValue(g, 'http://projects.bigasterisk.com/room/assignedIp')
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
50 except (KeyError, ValueError):
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
51 pass
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
52 raise ValueError
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
53
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
54
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
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
e9366f73e612 serve current usage as a graph
drewp@bigasterisk.com
parents: 723
diff changeset
59 self.graph = graph
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
62 line1, line2 = await getOneSseEvent('http://collector.default.svc.cluster.local/collector/graph/lanscape')
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
63 p = json.loads(line2.split(b'data: ', 1)[1])
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
64
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
65 deviceIp = ipLookup(p, deviceMac)
bacb13c10c7a lookup ip in a roundabout way
drewp@bigasterisk.com
parents: 1719
diff changeset
66
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
67 url = (f'http://{deviceIp}/cgi-bin/cgi_manager')
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
68
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
69 async with aiohttp.ClientSession() as session:
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
70 async with session.post(url,
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
71 headers={'Authorization': 'Basic %s' % auth.decode('ascii')},
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
72 data=(f'''<LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
73 <Name>get_usage_data</Name>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
74 <MacId>0x{macId}</MacId>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
75 </LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
76 <LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
77 <Name>get_price_blocks</Name>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
78 <MacId>0x{macId}</MacId>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
79 </LocalCommand>'''),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
80 timeout=10) as response:
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
81
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
82 ret = json.loads(await response.text())
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
83 log.debug(f"response body {ret}")
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
84 if ret['demand_units'] != 'kW':
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
85 raise ValueError
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
86 if ret['summation_units'] != 'kWh':
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
87 raise ValueError
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
88
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
89 demandW = float(ret['demand']) * 1000
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
90 self.out['w'].set(demandW)
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
91
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
92 sd = float(ret['summation_delivered'])
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
93 if sd > 0: # Sometimes nan
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
94 self.out['kwh'].set(sd)
734
e9366f73e612 serve current usage as a graph
drewp@bigasterisk.com
parents: 723
diff changeset
95
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
96 if 'price' in ret:
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
97 self.out['price'].set(float(ret['price']))
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
98
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
99 self.graph.patchObject(context=ROOM['powerEagle'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
100 subject=ROOM['housePower'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
101 predicate=ROOM['instantDemandWatts'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
102 newObject=Literal(demandW))
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
103
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
104
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
105 def main():
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
106 masterGraph = PatchableGraph()
311
ec6451f15ae5 powereagle reader writes to influxdb
drewp@bigasterisk.com
parents: 216
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
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
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
118
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
119 app = Starlette(debug=True,
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
120 routes=[
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
121 Route('/graph/power', StaticGraph(masterGraph)),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
122 Route('/graph/power/events', GraphEvents(masterGraph)),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
123 ])
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
124
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
125 app.add_middleware(PrometheusMiddleware, app_name='power_eagle')
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
126 app.add_route("/metrics", handle_metrics)
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
127 return app
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
128
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
129
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
130 app = main()