annotate service/powerEagle/reader.py @ 1754:92999dfbf321 default tip

add shelly support
author drewp@bigasterisk.com
date Tue, 04 Jun 2024 13:03:43 -0700
parents d012c53c5ae8
children
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
1748
d012c53c5ae8 powereagle now registers a hostname with dhcp so we don't have to hunt for it
drewp@bigasterisk.com
parents: 1723
diff changeset
17 from private_config import cloudId, deviceIp, 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
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
29 class Poller(object):
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
30
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
31 def __init__(self, out: Dict[str, Gauge], graph):
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
32 self.out = out
734
e9366f73e612 serve current usage as a graph
drewp@bigasterisk.com
parents: 723
diff changeset
33 self.graph = graph
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
34
1719
fb082013fa24 port to use my background_loop lib. various other dep updates.
drewp@bigasterisk.com
parents: 1714
diff changeset
35 async def poll(self, first_run: bool):
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
36 url = (f'http://{deviceIp}/cgi-bin/cgi_manager')
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
37
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
38 async with aiohttp.ClientSession() as session:
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
39 async with session.post(url,
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
40 headers={'Authorization': 'Basic %s' % auth.decode('ascii')},
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
41 data=(f'''<LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
42 <Name>get_usage_data</Name>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
43 <MacId>0x{macId}</MacId>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
44 </LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
45 <LocalCommand>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
46 <Name>get_price_blocks</Name>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
47 <MacId>0x{macId}</MacId>
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
48 </LocalCommand>'''),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
49 timeout=10) as response:
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
50
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
51 ret = json.loads(await response.text())
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
52 log.debug(f"response body {ret}")
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
53 if ret['demand_units'] != 'kW':
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
54 raise ValueError
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
55 if ret['summation_units'] != 'kWh':
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
56 raise ValueError
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
57
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
58 demandW = float(ret['demand']) * 1000
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
59 self.out['w'].set(demandW)
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
60
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
61 sd = float(ret['summation_delivered'])
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
62 if sd > 0: # Sometimes nan
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
63 self.out['kwh'].set(sd)
734
e9366f73e612 serve current usage as a graph
drewp@bigasterisk.com
parents: 723
diff changeset
64
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
65 if 'price' in ret:
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
66 self.out['price'].set(float(ret['price']))
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
67
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
68 self.graph.patchObject(context=ROOM['powerEagle'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
69 subject=ROOM['housePower'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
70 predicate=ROOM['instantDemandWatts'],
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
71 newObject=Literal(demandW))
211
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
72
7d3645c2157e rainforest eagle data -> carbon
drewp@bigasterisk.com
parents:
diff changeset
73
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
74 def main():
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
75 masterGraph = PatchableGraph()
311
ec6451f15ae5 powereagle reader writes to influxdb
drewp@bigasterisk.com
parents: 216
diff changeset
76
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
77 out = {
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
78 'w': Gauge('house_power_w', 'house power demand'),
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
79 'kwh': Gauge('house_power_kwh', 'house power sum delivered'),
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
80 'price': Gauge('house_power_price', 'house power price'),
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
81 }
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
82
786
e8654a3bd1c7 update powereagle for k8s and prometheus
drewp@bigasterisk.com
parents: 734
diff changeset
83 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
84
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
85 # 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
86 loop = background_loop.loop_forever(p.poll, periodSec)
1714
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
87
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
88 app = Starlette(debug=True,
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
89 routes=[
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
90 Route('/graph/power', StaticGraph(masterGraph)),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
91 Route('/graph/power/events', GraphEvents(masterGraph)),
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
92 ])
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
93
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
94 app.add_middleware(PrometheusMiddleware, app_name='power_eagle')
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
95 app.add_route("/metrics", handle_metrics)
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
96 return app
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
97
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
98
4cbe3df8f48f rewrite to use starlette/etc
drewp@bigasterisk.com
parents: 786
diff changeset
99 app = main()