2023-01-07 18:02:34 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import time
|
2023-12-16 22:36:53 +00:00
|
|
|
from logging import getLogger
|
2023-12-18 13:26:56 +00:00
|
|
|
from logging import root as logging_root
|
2023-12-16 22:36:53 +00:00
|
|
|
from logging.config import dictConfig as loggingDictConfig
|
2023-01-07 18:02:34 +00:00
|
|
|
|
2023-12-08 12:45:05 +00:00
|
|
|
from bluepy.btle import BTLEDisconnectError # type: ignore
|
|
|
|
from serial import SerialException # type: ignore
|
2023-01-07 18:02:34 +00:00
|
|
|
|
2023-04-07 21:57:37 +00:00
|
|
|
from .config import get_config, get_consumers, get_interface
|
2023-12-10 15:08:28 +00:00
|
|
|
from .protocol import ChargeController
|
2023-12-18 13:26:56 +00:00
|
|
|
from .util import LazyJSON, LoggingDictFilter, Periodical
|
2023-12-16 22:36:53 +00:00
|
|
|
|
2023-12-18 13:26:56 +00:00
|
|
|
logger = getLogger("SolarMPPT")
|
2023-01-07 18:02:34 +00:00
|
|
|
|
|
|
|
|
2023-04-07 21:57:37 +00:00
|
|
|
class CommunicationError(BTLEDisconnectError, SerialException, IOError):
|
2023-01-10 02:09:56 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2023-01-07 18:02:34 +00:00
|
|
|
def main():
|
|
|
|
conf = get_config()
|
2023-12-16 22:36:53 +00:00
|
|
|
|
|
|
|
loggingDictConfig(conf.get("logging", {}))
|
2023-12-18 13:26:56 +00:00
|
|
|
logging_dict_filter = LoggingDictFilter()
|
|
|
|
logging_dict_filter.data["service"] = "SolarMPPT"
|
|
|
|
logging_root.addFilter(logging_dict_filter)
|
|
|
|
|
2023-01-07 18:02:34 +00:00
|
|
|
consumers = get_consumers(conf)
|
|
|
|
|
|
|
|
per_voltages = Periodical(interval=15)
|
|
|
|
per_current_hist = Periodical(interval=60)
|
|
|
|
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
try:
|
2023-12-16 22:36:53 +00:00
|
|
|
logger.info("Connecting...")
|
2023-04-07 21:57:37 +00:00
|
|
|
with get_interface() as dev:
|
2023-12-18 13:26:56 +00:00
|
|
|
cc = ChargeController(dev)
|
|
|
|
logging_dict_filter.data["srne_model"] = cc.model
|
|
|
|
logging_dict_filter.data["srne_version"] = cc.version
|
|
|
|
logging_dict_filter.data["srne_serial"] = cc.serial
|
|
|
|
|
2023-12-16 22:36:53 +00:00
|
|
|
logger.info("Connected.")
|
2023-01-07 18:02:34 +00:00
|
|
|
|
2023-12-16 22:36:53 +00:00
|
|
|
logger.info(f"Controller model: {cc.model}")
|
|
|
|
logger.info(f"Controller version: {cc.version}")
|
|
|
|
logger.info(f"Controller serial: {cc.serial}")
|
2023-12-10 22:50:34 +00:00
|
|
|
for consumer in consumers:
|
|
|
|
consumer.controller = cc
|
2023-12-10 15:08:28 +00:00
|
|
|
|
2023-01-07 18:02:34 +00:00
|
|
|
# write(dev, construct_request(0, 32))
|
|
|
|
|
|
|
|
# Memory dump
|
|
|
|
# for address in range(0, 0x10000, 16):
|
|
|
|
# log(f"Reading 0x{address:04X}...")
|
|
|
|
# write(wd, construct_request(address, 16))
|
2023-12-10 15:08:28 +00:00
|
|
|
extra = cc.extra
|
|
|
|
days = extra.run_days
|
|
|
|
|
|
|
|
res = cc.today.as_dict()
|
|
|
|
res.update(extra.as_dict())
|
|
|
|
for consumer in consumers:
|
|
|
|
consumer.write(res)
|
|
|
|
del extra
|
2023-01-07 18:02:34 +00:00
|
|
|
|
2023-12-12 10:32:45 +00:00
|
|
|
# Historical data isn't actually used anywhere yet
|
|
|
|
# Limit to 4 days for now
|
|
|
|
for i in range(min(days, 4)):
|
2023-12-10 15:08:28 +00:00
|
|
|
hist = cc.get_historical(i)
|
|
|
|
res = hist.as_dict()
|
2023-12-18 13:26:56 +00:00
|
|
|
logger.debug(LazyJSON({i: res}))
|
2023-12-10 15:08:28 +00:00
|
|
|
for consumer in consumers:
|
|
|
|
consumer.write({str(i): res})
|
2023-01-07 18:02:34 +00:00
|
|
|
|
|
|
|
while True:
|
|
|
|
now = time.time()
|
|
|
|
|
|
|
|
if per_voltages(now):
|
2023-12-10 15:08:28 +00:00
|
|
|
data = cc.state.as_dict()
|
2023-12-18 13:26:56 +00:00
|
|
|
logger.debug(LazyJSON(data))
|
2023-12-10 15:08:28 +00:00
|
|
|
for consumer in consumers:
|
|
|
|
consumer.write(data)
|
2023-01-07 18:02:34 +00:00
|
|
|
|
|
|
|
if per_current_hist(now):
|
2023-12-10 15:08:28 +00:00
|
|
|
data = cc.today.as_dict()
|
|
|
|
data.update(cc.extra.as_dict())
|
2023-12-18 13:26:56 +00:00
|
|
|
logger.debug(LazyJSON(data))
|
2023-12-10 15:08:28 +00:00
|
|
|
for consumer in consumers:
|
|
|
|
consumer.write(data)
|
2023-01-07 18:02:34 +00:00
|
|
|
|
|
|
|
# print(".")
|
|
|
|
for consumer in consumers:
|
|
|
|
consumer.poll()
|
|
|
|
|
2023-12-10 15:08:28 +00:00
|
|
|
time.sleep(max(0, 1 - (time.time() - now)))
|
2023-01-07 18:02:34 +00:00
|
|
|
|
|
|
|
# if STATUS.get('load_enabled'):
|
|
|
|
# write(wd, CMD_DISABLE_LOAD)
|
|
|
|
# else:
|
|
|
|
# write(wd, CMD_ENABLE_LOAD)
|
|
|
|
|
2023-01-10 02:09:56 +00:00
|
|
|
except CommunicationError:
|
2023-12-16 22:36:53 +00:00
|
|
|
logger.error("Disconnected")
|
2023-01-07 18:02:34 +00:00
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
except (KeyboardInterrupt, SystemExit, Exception) as e:
|
|
|
|
for consumer in consumers:
|
|
|
|
consumer.exit()
|
|
|
|
|
|
|
|
if type(e) is not KeyboardInterrupt:
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|