11import argparse
2- import datetime
32import json
4- import time
5- import sys
63import logging
7- import itertools
4+ import sys
5+ import time
86
9- from rich import print_json
107from pylontech import *
11- from pymongo import MongoClient
8+ from pylontechpoller . reporter import MongoReporter , HassReporter
129
1310logger = logging .getLogger (__name__ )
1411
1512
1613def find_min_max_modules (modules ):
1714 all_voltages = []
15+ all_disbalances = []
16+
1817 for module in modules :
19- for voltage in module ["CellVoltages" ]:
20- all_voltages .append ((module ["NumberOfModule" ], voltage ))
18+ mid = module ["NumberOfModule" ]
19+ cvs = module ["CellVoltages" ]
20+ for voltage in cvs :
21+ all_voltages .append ((mid , voltage ))
22+ vmax = max (cvs )
23+ vmin = min (cvs )
24+ d = vmax - vmin
25+ all_disbalances .append ((mid , d ))
2126
2227 if not all_voltages :
2328 return None , None
2429
2530 min_pair = min (all_voltages , key = lambda x : x [1 ])
2631 max_pair = max (all_voltages , key = lambda x : x [1 ])
32+ max_disbalance = max (all_disbalances , key = lambda x : abs (x [1 ]))
2733
28- return min_pair , max_pair
34+ return min_pair , max_pair , max_disbalance
2935
3036
3137
@@ -48,22 +54,18 @@ def minimize_module(m: json) -> json:
4854 modules = b ["modules" ]
4955 find_min_max_modules (modules )
5056
51- (min_pair , max_pair ) = find_min_max_modules (modules )
52- # allcv = list(itertools.chain.from_iterable(map(lambda m: m["CellVoltages"], modules)))
53- # vmin = min(allcv)
54- # vmax = max(allcv)
57+ (min_pair , max_pair , max_disbalance ) = find_min_max_modules (modules )
5558
5659 return {
5760 "ts" : b ["timestamp" ],
5861 "cvmin" : min_pair ,
5962 "cvmax" : max_pair ,
6063 "stack_disbalance" : min_pair [1 ] - max_pair [1 ],
64+ "max_module_disbalance" : max_disbalance ,
6165 "modules" : list (map (minimize_module , modules )),
6266 }
6367
64- def mongo_cleanup (collection , retention_days ):
65- threshold = datetime .datetime .now () - datetime .timedelta (days = retention_days )
66- collection .delete_many ({"ts" : {"$lt" : threshold }})
68+
6769
6870def run (argv : list [str ]):
6971 parser = argparse .ArgumentParser (description = "Pylontech RS485 poller" )
@@ -75,11 +77,19 @@ def run(argv: list[str]):
7577 parser .add_argument ("--interval" , type = int , help = "polling interval in msec" , default = 1000 )
7678 parser .add_argument ("--retention-days" , type = int , help = "how long to retain history data" , default = 90 )
7779 parser .add_argument ("--debug" , type = bool , help = "verbose output" , default = False )
78- parser .add_argument ("--mongo-url" , type = str , help = "mongodb url" , default = False )
80+
81+ parser .add_argument ("--mongo-url" , type = str , help = "mongodb url" , default = None )
7982 parser .add_argument ("--mongo-db" , type = str , help = "target mongo database" , default = "pylontech" )
8083 parser .add_argument ("--mongo-collection-history" , type = str , help = "target mongo collection_hist for stack history" , default = "history" )
8184 parser .add_argument ("--mongo-collection-meta" , type = str , help = "target mongo collection_hist for stack data" , default = "meta" )
8285
86+ parser .add_argument ("--hass-url" , type = str , help = "hass url" , default = None )
87+ parser .add_argument ("--hass-stack-disbalance" , type = str , help = "state id" , default = "input_number.stack_disbalance" )
88+ parser .add_argument ("--hass-max-battery-disbalance" , type = str , help = "state id" , default = "input_number.max_bat_disbalance" )
89+ parser .add_argument ("--hass-max-battery-disbalance-id" , type = str , help = "state id" , default = "input_text.max_disbalance_id" )
90+ parser .add_argument ("--hass-token-file" , type = str , help = "hass token file" , default = "/var/run/agenix/hass-token" )
91+
92+
8393 args = parser .parse_args (argv [1 :])
8494
8595 level = logging .DEBUG if args .debug else logging .INFO
@@ -88,25 +98,42 @@ def run(argv: list[str]):
8898 cc = 0
8999 spinner = ['|' , '/' , '-' , '\\ ' ]
90100
101+ reporters = []
102+
91103 while True :
92104 try :
93105 logging .debug ("Preparing client..." )
94106 p = Pylontech (ExscriptTelnetTransport (host = args .source_host , port = args .source_port , timeout = args .timeout ))
95-
96- mongo = MongoClient (args .mongo_url )
97- db = mongo [args .mongo_db ]
98-
99- collection_meta = db [args .mongo_collection_meta ]
100107
101- collection_hist = db [args .mongo_collection_history ]
102- collection_hist .create_index ("ts" , expireAfterSeconds = 3600 * 24 * 90 )
108+ mongo_url = args .mongo_url
109+
110+ if mongo_url :
111+ reporters .append (MongoReporter (
112+ mongo_url ,
113+ args .mongo_db ,
114+ args .mongo_collection_meta ,
115+ args .mongo_collection_history ,
116+ args .retention_days
117+ ))
118+
119+ hass_url = args .hass_url
120+ print (hass_url )
121+ if hass_url :
122+ reporters .append (HassReporter (
123+ hass_url ,
124+ args .hass_stack_disbalance ,
125+ args .hass_max_battery_disbalance ,
126+ args .hass_max_battery_disbalance_id ,
127+ args .hass_token_file
128+ ))
103129
104130 logging .info ("About to start polling..." )
105131 bats = p .scan_for_batteries (2 , 10 )
106132
107133 logging .info ("Have battery stack data" )
108- collection_meta .insert_one ({'ts' : datetime .datetime .now ().isoformat (), "stack" : to_json_serializable (bats )})
109134
135+ for reporter in reporters :
136+ reporter .report_meta (bats )
110137
111138 for b in p .poll_parameters (bats .range ()):
112139 cc += 1
@@ -115,11 +142,14 @@ def run(argv: list[str]):
115142 sys .stdout .write ('\r ' + spinner [cc % len (spinner )])
116143 sys .stdout .flush ()
117144
145+ mb = minimize (b )
118146 # print(print_json(json.dumps(minimize(b))))
119- collection_hist .insert_one (minimize (b ))
147+ for reporter in reporters :
148+ reporter .report_state (mb )
120149
121150 if cc % 86400 == 0 :
122- mongo_cleanup (collection_hist , args .retention_days )
151+ for reporter in reporters :
152+ reporter .cleanup ()
123153
124154 time .sleep (args .interval / 1000.0 )
125155 except (KeyboardInterrupt , SystemExit ):
0 commit comments