bms/berrybms.py
2025-10-14 20:47:40 -05:00

155 lines
4.8 KiB
Python

#!/usr/bin/python3
#
# Copyright (C) 2025 Extrafu <extrafu@gmail.com>
#
# This file is part of BerryBMS.
#
# BerryBMS is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any
# later version.
#
import sys
import time
import yaml
import logging
import json
import signal
import paho.mqtt.client as paho
from ConextAGS import ConextAGS
from ConextInsightHome import ConextInsightHome
from JKBMS import JKBMS
from Register import Register
# Global variables to enable cleanups in signal handler
all_modbus_devices = []
paho_client = None
def cleanup(_signo, _stack_frame):
print("Cleaning up before being terminated!")
global paho_client
if paho_client != None:
paho_client.disconnect()
global all_modbus_devices
for device in all_modbus_devices:
device.disconnect()
sys.exit(0)
def main(daemon):
# setup the signal handler
signal.signal(signal.SIGTERM, cleanup)
# load yaml config
f = open("config.yaml","r")
config = yaml.load(f, Loader=yaml.SafeLoader)
while True:
# Connect to MQTT server. We do it each time since it's not costly
# and avoids long keepalive if the updateinterval is set to a high value.
global paho_client
paho_client = paho.Client()
try:
paho_client.connect(config['mqtt']['host'], int(config['mqtt']['port']), 60)
except:
print("Couldn't connect to the MQTT broker, the GUI part if used, won't be updated.")
paho_client = None
global all_modbus_devices
all_devices = {}
all_bms = config['bms']
average_voltage = 0
average_soc = 0
active_bms = 0
total_used_capacity = 0
highest_soc = 0
highest_id = 0
lowest_soc = 100
lowest_id = 0
if all_bms != None:
for key in all_bms.keys():
bms = all_bms[key]
bms_id = bms['id']
bms_port = bms['port']
jkbms = JKBMS(key, bms_id, bms_port)
c = jkbms.connect()
if c == None:
continue
#print(jkbms)
print(jkbms.formattedOutput(), '\n')
all_modbus_devices.append(jkbms)
active_bms += 1
soc = jkbms.getRegister('SOCStateOfcharge').value & 0x0FF
average_soc += soc;
# We adjust the lowest/highest SOC
if soc > highest_soc:
highest_soc = soc
highest_id = bms_id
if lowest_soc > soc:
lowest_soc = soc
lowest_id = bms_id
average_voltage += jkbms.getRegister('BatVol').value
total_used_capacity += (jkbms.getRegister('SOCFullChargeCap').value - jkbms.getRegister('SOCCapRemain').value)
# Publish all BMS values in MQTT
jkbms.publish(all_devices)
if config['insighthome'] != None:
conext = ConextInsightHome(config['insighthome']['host'], config['insighthome']['port'], config['insighthome'].get('ids', None))
all_modbus_devices.append(conext)
c = conext.connect()
devices = conext.allDevices()
for device in devices:
device.publish(all_devices)
#print("%s: %s\n" % (type(device).__name__, device))
print(device.formattedOutput(), '\n')
# Publish all values in MQTT
if paho_client != None:
paho_client.publish("berrybms", json.dumps(all_devices))
paho_client.disconnect()
# We disconnect from all Modbus devices
for device in all_modbus_devices:
device.disconnect()
all_modbus_devices = []
print("== Global BMS Statistics ==")
average_voltage = average_voltage/active_bms
average_soc = average_soc/active_bms
print(f"Average Voltage:\t{average_voltage:.2f}v")
print(f"Average SOC:\t\t{average_soc:.1f}%")
print(f"Total Used Capacity:\t{total_used_capacity:.2f} Ah (~ {(total_used_capacity*average_voltage/1000):.2f} KWh)")
print(f"Lowest SOC:\t\t{lowest_id} ({lowest_soc}%) Highest: {highest_id} ({highest_soc}%)")
if daemon:
updateinterval = config.get('updateinterval')
if updateinterval == None:
updateinterval = 30
print(f'Sleeping for {updateinterval} seconds...')
time.sleep(updateinterval)
else:
break
if __name__ == "__main__":
args = sys.argv[1:]
daemon = False
#print(args)
if len(args) == 1 and args[0] == '-d':
daemon = True
main(daemon)