From fe6b89cfb2a1623bd835d60f76b5bcd9102c684d Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 10 Dec 2022 23:10:26 +0000 Subject: [PATCH 001/209] first commits for jkbms ble integration --- etc/dbus-serialbattery/jkbms_ble.py | 139 ++++++++++++++++++++++++++++ etc/dbus-serialbattery/utils.py | 1 + 2 files changed, 140 insertions(+) create mode 100644 etc/dbus-serialbattery/jkbms_ble.py diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py new file mode 100644 index 00000000..7535d114 --- /dev/null +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +from battery import Protection, Battery, Cell +from utils import * +from struct import * +from jkbms import JkBmsBle +from bleak import BleakScanner + +class Jkbms_Ble(Battery): + BATTERYTYPE = "Jkbms BLE" + def __init__(self, port,baud, address): + super(Jkbms_Ble, self).__init__(port,baud) + self.type = self.BATTERYTYPE + self.jk = JkBmsBLE(address) + + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + + # check if device with given mac is found, otherwise abort + devices = await BleakScanner.discover() + found=False + for d in devices + if d.address=self.jk.address + found=True + if not found: + return False + + #device was found, presumeably a jkbms so start scraping + jk.start_scraping() + tries = 1 + + while jk.get_status() == None and tries < 20: + time.sleep(0.5) + tries += 1 + + # load initial data, from here on get_status has valid values to be served to the dbus + status=jk.get_status() + if status == None: + return False + + if not status["device_info"]["vendor_id"].startswith("JK-") + return False + + logger.info("JK BMS found!") + return true + + def get_settings(self): + # After successful connection get_settings will be call to set up the battery. + # Set the current limits, populate cell count, etc + # Return True if success, False for failure + + # Uncomment if BMS does not supply capacity + # self.capacity = BATTERY_CAPACITY + st=jk.get_status()["settings"] + + self.cell_count=st["cell_count"] + self.max_battery_charge_current = st["max_charge_current"] + self.max_battery_discharge_current = status["max_discharge_current"] + self.max_battery_voltage = st["cell_ovp"] * self.cell_count + self.min_battery_voltage = st["cell_uvp"] * self.cell_count + + for c in range(self.cell_count): + self.cells.append(Cell(False)) + + self.hardware_version = "JKBMS "+ jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" + + + + return True + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (1 second) + # Return True if success, False for failure + + #result = self.read_soc_data() + #TODO: check for errors + status=jk.get_status() + if status == None: + return False + if time.time() - status["last_update"] > 30: + #if data not updated for more than 30s, sth is wrong, then fail + return False + + for c in range(self.cell_count): + self.cells[c].voltage=status["cell_info"]["voltages"][c] + + self.to_temp(1, status["cell_info"]["temperature_sensor_1"]) + self.to_temp(2, status["cell_info"]["temperature_sensor_2"]) + self.current=status["cell_info"]["current"] + self.voltage=status["cell_info"]["voltage"] + + self.soc=status["cell_info"]["battery_soc"] + self.cycles=status["cell_info"]["cycle_count"] + self.capacity=status["cell_info"]["nominal_capacity"] + return True + + + def read_status_data(self): + status_data = self.read_serial_data_template(self.command_status) + # check if connection success + if status_data is False: + return False + + self.cell_count, self.temp_sensors, self.charger_connected, self.load_connected, \ + state, self.cycles = unpack_from('>bb??bhx', status_data) + + self.hardware_version = "TemplateBMS " + str(self.cell_count) + " cells" + logger.info(self.hardware_version) + return True + + def read_soc_data(self): + soc_data = self.read_serial_data_template(self.command_soc) + # check if connection success + if soc_data is False: + return False + + voltage, current, soc = unpack_from('>hxxhh', soc_data) + self.voltage = voltage / 10 + self.current = current / -10 + self.soc = soc / 10 + return True + + def read_serial_data_template(self, command): + # use the read_serial_data() function to read the data and then do BMS spesific checks (crc, start bytes, etc) + data = read_serial_data(command, self.port, self.baud_rate, self.LENGTH_POS, self.LENGTH_CHECK) + if data is False: + return False + + start, flag, command_ret, length = unpack_from('BBBB', data) + checksum = sum(data[:-1]) & 0xFF + + if start == 165 and length == 8 and checksum == data[12]: + return data[4:length+4] + else: + logger.error(">>> ERROR: Incorrect Reply") + return False diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 1a5a4a9c..1f8aed86 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -17,6 +17,7 @@ {"bms" : "Daly", "address" : b"\x40"}, {"bms" : "Daly", "address" : b"\x80"}, {"bms" : "Jkbms", "baud" : 115200}, + {"bms" : "Jkbms_Ble" "address" : "C8:47:8C:E4:54:0E"}, {"bms" : "Sinowealth"}, {"bms" : "Lifepower"}, {"bms" : "Renogy", "address": b"\x30"}, From 6a68d3c96aaaf62722b91f867f9aed01c65fc3f9 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sun, 25 Dec 2022 20:38:47 +0000 Subject: [PATCH 002/209] implemented most warnings except undercurrent(?), imbalance --- etc/dbus-serialbattery/jkbms_ble.py | 83 +++++++++-------------------- 1 file changed, 25 insertions(+), 58 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 7535d114..d8b7473c 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -50,11 +50,7 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - - # Uncomment if BMS does not supply capacity - # self.capacity = BATTERY_CAPACITY st=jk.get_status()["settings"] - self.cell_count=st["cell_count"] self.max_battery_charge_current = st["max_charge_current"] self.max_battery_discharge_current = status["max_discharge_current"] @@ -66,8 +62,6 @@ def get_settings(self): self.hardware_version = "JKBMS "+ jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" - - return True def refresh_data(self): @@ -77,63 +71,36 @@ def refresh_data(self): #result = self.read_soc_data() #TODO: check for errors - status=jk.get_status() + st=jk.get_status() if status == None: return False - if time.time() - status["last_update"] > 30: + if time.time() - st["last_update"] > 30: #if data not updated for more than 30s, sth is wrong, then fail return False for c in range(self.cell_count): - self.cells[c].voltage=status["cell_info"]["voltages"][c] + self.cells[c].voltage=st["cell_info"]["voltages"][c] - self.to_temp(1, status["cell_info"]["temperature_sensor_1"]) - self.to_temp(2, status["cell_info"]["temperature_sensor_2"]) - self.current=status["cell_info"]["current"] - self.voltage=status["cell_info"]["voltage"] - - self.soc=status["cell_info"]["battery_soc"] - self.cycles=status["cell_info"]["cycle_count"] - self.capacity=status["cell_info"]["nominal_capacity"] - return True - - - def read_status_data(self): - status_data = self.read_serial_data_template(self.command_status) - # check if connection success - if status_data is False: - return False - - self.cell_count, self.temp_sensors, self.charger_connected, self.load_connected, \ - state, self.cycles = unpack_from('>bb??bhx', status_data) - - self.hardware_version = "TemplateBMS " + str(self.cell_count) + " cells" - logger.info(self.hardware_version) - return True - - def read_soc_data(self): - soc_data = self.read_serial_data_template(self.command_soc) - # check if connection success - if soc_data is False: - return False - - voltage, current, soc = unpack_from('>hxxhh', soc_data) - self.voltage = voltage / 10 - self.current = current / -10 - self.soc = soc / 10 + self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) + self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) + self.current=st["cell_info"]["current"] + self.voltage=st["cell_info"]["voltage"] + + self.soc=st["cell_info"]["battery_soc"] + self.cycles=st["cell_info"]["cycle_count"] + self.capacity=st["cell_info"]["nominal_capacity"] + + #protection bits + #self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 + #self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 + + self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 + self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 + + self.protection.current_over = 2 if (st["warnings"]["charge_overcurrent"] or st["warnings"]["discharge_overcurrent"]) else 0 + + self.protection.set_IC_inspection = 2 if st["warnings"]["temperature_mos"] else 0 + self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0 + self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0 + self.protection.temp_high_discharge = 2 if st["warnings"]["discharge_overtemp"] else 0 return True - - def read_serial_data_template(self, command): - # use the read_serial_data() function to read the data and then do BMS spesific checks (crc, start bytes, etc) - data = read_serial_data(command, self.port, self.baud_rate, self.LENGTH_POS, self.LENGTH_CHECK) - if data is False: - return False - - start, flag, command_ret, length = unpack_from('BBBB', data) - checksum = sum(data[:-1]) & 0xFF - - if start == 165 and length == 8 and checksum == data[12]: - return data[4:length+4] - else: - logger.error(">>> ERROR: Incorrect Reply") - return False From f214263f2b1b7821f7cac08cf7b34140cc0158e1 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sun, 25 Dec 2022 22:44:22 +0000 Subject: [PATCH 003/209] getting things together, removed most obvious mistakes; connection test is executed, further work required --- buildfiles.lst | 3 +- etc/dbus-serialbattery/dbus-serialbattery.py | 1 + etc/dbus-serialbattery/jkbms_ble.py | 34 ++++++++++++-------- etc/dbus-serialbattery/utils.py | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/buildfiles.lst b/buildfiles.lst index eb59db07..b1904116 100644 --- a/buildfiles.lst +++ b/buildfiles.lst @@ -24,8 +24,9 @@ etc/dbus-serialbattery/ant.py etc/dbus-serialbattery/util_max17853.py etc/dbus-serialbattery/mnb.py etc/dbus-serialbattery/jkbms.py +etc/dbus-serialbattery/jkbms_ble.py etc/dbus-serialbattery/sinowealth.py etc/dbus-serialbattery/renogy.py etc/dbus-serialbattery/revov.py etc/dbus-serialbattery/ecs.py -etc/dbus-serialbattery/lifepower.py \ No newline at end of file +etc/dbus-serialbattery/lifepower.py diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 60fe5f00..614ecff3 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -20,6 +20,7 @@ from daly import Daly from ant import Ant from jkbms import Jkbms +from jkbms_ble import Jkbms_Ble from sinowealth import Sinowealth from renogy import Renogy from revov import Revov diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index d8b7473c..b791cd94 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -2,16 +2,19 @@ from battery import Protection, Battery, Cell from utils import * from struct import * -from jkbms import JkBmsBle +from jkbms_brn import JkBmsBle from bleak import BleakScanner +import asyncio +import time class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" def __init__(self, port,baud, address): super(Jkbms_Ble, self).__init__(port,baud) self.type = self.BATTERYTYPE - self.jk = JkBmsBLE(address) + self.jk = JkBmsBle(address) + logger.error("init of jkbmsble") def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. @@ -19,38 +22,43 @@ def test_connection(self): # Return True if success, False for failure # check if device with given mac is found, otherwise abort - devices = await BleakScanner.discover() + + logger.error("test of jkbmsble") + loop = asyncio.get_event_loop() + t = loop.create_task(BleakScanner.discover()) + devices = loop.run_until_complete(t) + found=False - for d in devices - if d.address=self.jk.address + for d in devices: + if d.address == self.jk.address: found=True if not found: return False #device was found, presumeably a jkbms so start scraping - jk.start_scraping() + self.jk.start_scraping() tries = 1 - while jk.get_status() == None and tries < 20: + while self.jk.get_status() == None and tries < 20: time.sleep(0.5) tries += 1 # load initial data, from here on get_status has valid values to be served to the dbus - status=jk.get_status() + status=self.jk.get_status() if status == None: return False - if not status["device_info"]["vendor_id"].startswith("JK-") + if not status["device_info"]["vendor_id"].startswith("JK-"): return False logger.info("JK BMS found!") - return true + return True def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - st=jk.get_status()["settings"] + st=self.jk.get_status()["settings"] self.cell_count=st["cell_count"] self.max_battery_charge_current = st["max_charge_current"] self.max_battery_discharge_current = status["max_discharge_current"] @@ -61,7 +69,7 @@ def get_settings(self): self.cells.append(Cell(False)) self.hardware_version = "JKBMS "+ jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" - + logger.info("BAT: "+self.hardware_version) return True def refresh_data(self): @@ -71,7 +79,7 @@ def refresh_data(self): #result = self.read_soc_data() #TODO: check for errors - st=jk.get_status() + st=self.jk.get_status() if status == None: return False if time.time() - st["last_update"] > 30: diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 1f8aed86..b3ebb8a3 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -17,7 +17,7 @@ {"bms" : "Daly", "address" : b"\x40"}, {"bms" : "Daly", "address" : b"\x80"}, {"bms" : "Jkbms", "baud" : 115200}, - {"bms" : "Jkbms_Ble" "address" : "C8:47:8C:E4:54:0E"}, + {"bms" : "Jkbms_Ble", "address" : "C8:47:8C:E4:54:0E"}, {"bms" : "Sinowealth"}, {"bms" : "Lifepower"}, {"bms" : "Renogy", "address": b"\x30"}, From cb5f8657eea8a3109fdc611e668e376d6b7d434f Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Mon, 26 Dec 2022 20:55:07 +0000 Subject: [PATCH 004/209] sync to upstream --- etc/dbus-serialbattery/jkbms_ble.py | 139 ++++++++++++++++++++++++++++ etc/dbus-serialbattery/utils.py | 1 + 2 files changed, 140 insertions(+) create mode 100644 etc/dbus-serialbattery/jkbms_ble.py diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py new file mode 100644 index 00000000..7535d114 --- /dev/null +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +from battery import Protection, Battery, Cell +from utils import * +from struct import * +from jkbms import JkBmsBle +from bleak import BleakScanner + +class Jkbms_Ble(Battery): + BATTERYTYPE = "Jkbms BLE" + def __init__(self, port,baud, address): + super(Jkbms_Ble, self).__init__(port,baud) + self.type = self.BATTERYTYPE + self.jk = JkBmsBLE(address) + + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + + # check if device with given mac is found, otherwise abort + devices = await BleakScanner.discover() + found=False + for d in devices + if d.address=self.jk.address + found=True + if not found: + return False + + #device was found, presumeably a jkbms so start scraping + jk.start_scraping() + tries = 1 + + while jk.get_status() == None and tries < 20: + time.sleep(0.5) + tries += 1 + + # load initial data, from here on get_status has valid values to be served to the dbus + status=jk.get_status() + if status == None: + return False + + if not status["device_info"]["vendor_id"].startswith("JK-") + return False + + logger.info("JK BMS found!") + return true + + def get_settings(self): + # After successful connection get_settings will be call to set up the battery. + # Set the current limits, populate cell count, etc + # Return True if success, False for failure + + # Uncomment if BMS does not supply capacity + # self.capacity = BATTERY_CAPACITY + st=jk.get_status()["settings"] + + self.cell_count=st["cell_count"] + self.max_battery_charge_current = st["max_charge_current"] + self.max_battery_discharge_current = status["max_discharge_current"] + self.max_battery_voltage = st["cell_ovp"] * self.cell_count + self.min_battery_voltage = st["cell_uvp"] * self.cell_count + + for c in range(self.cell_count): + self.cells.append(Cell(False)) + + self.hardware_version = "JKBMS "+ jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" + + + + return True + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (1 second) + # Return True if success, False for failure + + #result = self.read_soc_data() + #TODO: check for errors + status=jk.get_status() + if status == None: + return False + if time.time() - status["last_update"] > 30: + #if data not updated for more than 30s, sth is wrong, then fail + return False + + for c in range(self.cell_count): + self.cells[c].voltage=status["cell_info"]["voltages"][c] + + self.to_temp(1, status["cell_info"]["temperature_sensor_1"]) + self.to_temp(2, status["cell_info"]["temperature_sensor_2"]) + self.current=status["cell_info"]["current"] + self.voltage=status["cell_info"]["voltage"] + + self.soc=status["cell_info"]["battery_soc"] + self.cycles=status["cell_info"]["cycle_count"] + self.capacity=status["cell_info"]["nominal_capacity"] + return True + + + def read_status_data(self): + status_data = self.read_serial_data_template(self.command_status) + # check if connection success + if status_data is False: + return False + + self.cell_count, self.temp_sensors, self.charger_connected, self.load_connected, \ + state, self.cycles = unpack_from('>bb??bhx', status_data) + + self.hardware_version = "TemplateBMS " + str(self.cell_count) + " cells" + logger.info(self.hardware_version) + return True + + def read_soc_data(self): + soc_data = self.read_serial_data_template(self.command_soc) + # check if connection success + if soc_data is False: + return False + + voltage, current, soc = unpack_from('>hxxhh', soc_data) + self.voltage = voltage / 10 + self.current = current / -10 + self.soc = soc / 10 + return True + + def read_serial_data_template(self, command): + # use the read_serial_data() function to read the data and then do BMS spesific checks (crc, start bytes, etc) + data = read_serial_data(command, self.port, self.baud_rate, self.LENGTH_POS, self.LENGTH_CHECK) + if data is False: + return False + + start, flag, command_ret, length = unpack_from('BBBB', data) + checksum = sum(data[:-1]) & 0xFF + + if start == 165 and length == 8 and checksum == data[12]: + return data[4:length+4] + else: + logger.error(">>> ERROR: Incorrect Reply") + return False diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 8becb88b..85ae28c0 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -18,6 +18,7 @@ {"bms" : "Daly", "address" : b"\x40"}, {"bms" : "Daly", "address" : b"\x80"}, {"bms" : "Jkbms", "baud" : 115200}, + {"bms" : "Jkbms_Ble" "address" : "C8:47:8C:E4:54:0E"}, # {"bms" : "Sinowealth"}, {"bms" : "Lifepower"}, {"bms" : "Renogy", "address": b"\x30"}, From fcb8a17c5b2d1646a00ba6a82fb74141de0f4f28 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Mon, 26 Dec 2022 21:37:20 +0000 Subject: [PATCH 005/209] bugfixes, working on manual run :) --- etc/dbus-serialbattery/jkbms_ble.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index b791cd94..14a50121 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -61,14 +61,14 @@ def get_settings(self): st=self.jk.get_status()["settings"] self.cell_count=st["cell_count"] self.max_battery_charge_current = st["max_charge_current"] - self.max_battery_discharge_current = status["max_discharge_current"] + self.max_battery_discharge_current = st["max_discharge_current"] self.max_battery_voltage = st["cell_ovp"] * self.cell_count self.min_battery_voltage = st["cell_uvp"] * self.cell_count for c in range(self.cell_count): self.cells.append(Cell(False)) - self.hardware_version = "JKBMS "+ jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" + self.hardware_version = "JKBMS "+ self.jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" logger.info("BAT: "+self.hardware_version) return True @@ -80,7 +80,7 @@ def refresh_data(self): #result = self.read_soc_data() #TODO: check for errors st=self.jk.get_status() - if status == None: + if st == None: return False if time.time() - st["last_update"] > 30: #if data not updated for more than 30s, sth is wrong, then fail @@ -92,11 +92,11 @@ def refresh_data(self): self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) self.current=st["cell_info"]["current"] - self.voltage=st["cell_info"]["voltage"] + self.voltage=st["cell_info"]["total_voltage"] self.soc=st["cell_info"]["battery_soc"] self.cycles=st["cell_info"]["cycle_count"] - self.capacity=st["cell_info"]["nominal_capacity"] + self.capacity=st["cell_info"]["capacity_nominal"] #protection bits #self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 @@ -107,7 +107,7 @@ def refresh_data(self): self.protection.current_over = 2 if (st["warnings"]["charge_overcurrent"] or st["warnings"]["discharge_overcurrent"]) else 0 - self.protection.set_IC_inspection = 2 if st["warnings"]["temperature_mos"] else 0 + self.protection.set_IC_inspection = 2 if st["cell_info"]["temperature_mos"] > 80 else 0 self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0 self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0 self.protection.temp_high_discharge = 2 if st["warnings"]["discharge_overtemp"] else 0 From 46c918b69a664eadcf71b9f29c8570bcdeb149a8 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Wed, 28 Dec 2022 00:04:27 +0000 Subject: [PATCH 006/209] give the bluetooth-jkbms a dummy-serialport that will fail to open, so that the bt-connection can be tested --- etc/dbus-serialbattery/jkbms_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 14a50121..e54cf924 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -10,7 +10,7 @@ class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" def __init__(self, port,baud, address): - super(Jkbms_Ble, self).__init__(port,baud) + super(Jkbms_Ble, self).__init__("zero",baud) self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) From 57cee54f056a7a5a5504997047b5695f8d820044 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Fri, 6 Jan 2023 22:37:24 +0000 Subject: [PATCH 007/209] added installation dependencies to installlocal; added error-handling if no bt-device is available during test_connection --- buildfiles.lst | 1 + etc/dbus-serialbattery/installlocal.sh | 7 +- etc/dbus-serialbattery/jkbms_brn.py | 385 +++++++++++++++++++++++++ 3 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 etc/dbus-serialbattery/jkbms_brn.py diff --git a/buildfiles.lst b/buildfiles.lst index 2b93e965..fe7809b0 100644 --- a/buildfiles.lst +++ b/buildfiles.lst @@ -25,6 +25,7 @@ etc/dbus-serialbattery/util_max17853.py etc/dbus-serialbattery/mnb.py etc/dbus-serialbattery/jkbms.py etc/dbus-serialbattery/jkbms_ble.py +etc/dbus-serialbattery/jkbms_brn.py etc/dbus-serialbattery/sinowealth.py etc/dbus-serialbattery/renogy.py etc/dbus-serialbattery/ecs.py diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index b389ccd2..76b1d6b5 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,3 +1,8 @@ #!/bin/sh +opkg update +opkg install python3-misc python3-pip +pip3 install bleak tar -zxf ./venus-data.tar.gz -C /data -sh /data/etc/dbus-serialbattery/reinstalllocal.sh \ No newline at end of file +sh /data/etc/dbus-serialbattery/reinstalllocal.sh +echo "make sure to disable Settings/Bluetooth in the Remote-Console to prevent reconnects every minute. In case of crash after ~12-16 hours disable raspberry pi 3 internal bluetooth via dtoverlay and use an external usb bluetooth-dongle" + diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py new file mode 100644 index 00000000..f25a0c41 --- /dev/null +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -0,0 +1,385 @@ +import asyncio +from bleak import BleakScanner, BleakClient +import time +from logging import info, debug +import logging +from struct import unpack_from,calcsize +import threading +logging.basicConfig(level=logging.INFO) + + +# zero means parse all incoming data (every second) +CELL_INFO_REFRESH_S = 0 +DEVICE_INFO_REFRESH_S = 60*60*5 #every 5 Hours +CHAR_HANDLE="0000ffe1-0000-1000-8000-00805f9b34fb" +MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" + +COMMAND_CELL_INFO = 0x96; +COMMAND_DEVICE_INFO = 0x97; + +FRAME_VERSION_JK04 = 0x01 +FRAME_VERSION_JK02 = 0x02 +FRAME_VERSION_JK02_32S = 0x03 +PROTOCOL_VERSION_JK02 = 0x02 + + +protocol_version=PROTOCOL_VERSION_JK02 + + +MIN_RESPONSE_SIZE = 300; +MAX_RESPONSE_SIZE = 320; + +TRANSLATE_DEVICE_INFO = [ + [["device_info","hw_rev"],22,"8s"], + [["device_info","sw_rev"],30,"8s"], + [["device_info","uptime"],38," CELL_INFO_REFRESH_S: + self.last_cell_info=time.time() + info("processing frame with battery cell info") + if protocol_version == PROTOCOL_VERSION_JK02: + self.decode_cellinfo_jk02() + self.bms_status["last_update"]=time.time() + #overriding special values + ## power is calculated from voltage x current as register 122 contains unsigned power-value + self.decode_cellinfo_jk02() + self.bms_status["last_update"]=time.time() + #overriding special values + ## power is calculated from voltage x current as register 122 contains unsigned power-value + self.bms_status["cell_info"]["power"]=self.bms_status["cell_info"]["current"]*self.bms_status["cell_info"]["total_voltage"] + if self.waiting_for_response=="cell_info": + self.waiting_for_response="" + + elif info_type == 0x03: + + info("processing frame with device info") + if protocol_version == PROTOCOL_VERSION_JK02: + self.decode_device_info_jk02() + self.bms_status["last_update"]=time.time() + else: + return + if self.waiting_for_response=="device_info": + self.waiting_for_response="" + + def assemble_frame(self, data:bytearray): + #global frame_buffer + if len(self.frame_buffer) > MAX_RESPONSE_SIZE: + info("data dropped because it alone was longer than max frame length") + self.frame_buffer=[] + + if data[0] == 0x55 and data[1] == 0xAA and data[2] == 0xEB and data[3] == 0x90: + #beginning of new frame, clear buffer + self.frame_buffer=[] + + self.frame_buffer.extend(data) + + if len(self.frame_buffer) >= MIN_RESPONSE_SIZE: + #check crc; always at position 300, independent of actual frame-lentgh, so crc up to 299 + ccrc=self.crc(self.frame_buffer,300-1) + rcrc=self.frame_buffer[300-1] + debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") + if ccrc == rcrc: + debug("great success! frame complete and sane, lets go decoding") + self.decode() + self.frame_buffer=[] + + def ncallback(self, sender: int, data: bytearray): + debug(f"------> NEW PACKAGE!laenge: {len(data)}") + self.assemble_frame(data) + + def crc(self, arr:bytearray, length: int) -> int: + crc = 0; + for a in arr[:length]: + crc = crc + a; + return crc.to_bytes(2, 'little')[0] + + async def write_register(self, address, vals :bytearray,length:int, bleakC:BleakClient): + frame = bytearray(20) + frame[0] = 0xAA #start sequence + frame[1] = 0x55 #start sequence + frame[2] = 0x90 #start sequence + frame[3] = 0xEB #start sequence + frame[4] = address #holding register + frame[5] = length #size of the value in byte + frame[6] = vals[0] + frame[7] = vals[1] + frame[8] = vals[2] + frame[9] = vals[3] + frame[10] = 0x00 + frame[11] = 0x00 + frame[12] = 0x00 + frame[13] = 0x00 + frame[14] = 0x00 + frame[15] = 0x00 + frame[16] = 0x00 + frame[17] = 0x00 + frame[18] = 0x00 + + frame[19] = self.crc(frame,len(frame)-1) + debug( "Write register: ",frame ) + await bleakC.write_gatt_char(CHAR_HANDLE,frame, False) + + + + async def request_bt(self, rtype :str, client): + #global waiting_for_response + + + timeout = time.time() + + while self.waiting_for_response!="" and time.time()-timeout < 10 : + await asyncio.sleep(1) + print(self.waiting_for_response) + + + if rtype=="cell_info": + cmd=COMMAND_CELL_INFO + self.waiting_for_response="cell_info" + elif rtype=="device_info": + cmd=COMMAND_DEVICE_INFO + self.waiting_for_response="device_info" + else: + return + + await self.write_register(cmd,b'\0\0\0\0',0x00,client) + + def get_status(self): + if "settings" in self.bms_status and "cell_info" in self.bms_status: + return self.bms_status + else: + return None + + def connect_and_scrape(self): + asyncio.run(self.asy_connect_and_scrape()) + + async def asy_connect_and_scrape(self): + print("connect and scrape on address: "+self.address) + self.run = True + while self.run and self.main_thread.is_alive(): #autoreconnect + client = BleakClient(self.address) + print("btloop") + try: + await client.connect() + self.bms_status["model_nbr"]= (await client.read_gatt_char(MODEL_NBR_UUID)).decode("utf-8") + + await client.start_notify(CHAR_HANDLE, self.ncallback) + await self.request_bt("device_info", client) + + await self.request_bt("cell_info", client) + # await self.enable_charging(client) + last_dev_info=time.time() + while client.is_connected and self.run and self.main_thread.is_alive(): + if time.time()-last_dev_info>DEVICE_INFO_REFRESH_S: + last_dev_info=time.time() + await self.request_bt("device_info", client) + await asyncio.sleep(0.01) + except Exception as e: + info("error while connecting to bt: " + str(e)) + self.run=False + finally: + await client.disconnect() + + print("Exiting bt-loop") + + + def start_scraping(self): + self.main_thread=threading.current_thread() + if self.is_running(): + return + self.bt_thread.start() + info("scraping thread started -> main thread id: "+str(self.main_thread.ident)+" scraping thread: "+str(self.bt_thread.ident)) + + def stop_scraping(self): + self.run=False + while is_running(): + time.sleep(0.1) + + def is_running(self): + return self.bt_thread.is_alive() + + async def enable_charging(self,c): + # these are the registers for the control-buttons; data is 01 00 00 00 for on 00 00 00 00 for off; the following bytes up to 19 are unclear and changing dynamically -> auth-mechanism? + await self.write_register(0x1d,b'\x01\x00\x00\x00',4,c) + await self.write_register(0x1e,b'\x01\x00\x00\x00',4,c) + await self.write_register(0x1f,b'\x01\x00\x00\x00',4,c) + await self.write_register(0x40,b'\x01\x00\x00\x00',4,c) + +if __name__ == "__main__": + jk = JkBmsBle("C8:47:8C:E4:54:0E") + info("sss") + jk.start_scraping() + while True: + print("asdf") + print(jk.get_status()) + time.sleep(5) From 4555bb00f7c955dbac83a3c65cd4bfa31b6c07da Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 16:07:37 +0000 Subject: [PATCH 008/209] tidy up code --- etc/dbus-serialbattery/jkbms_brn.py | 406 +++++++++++++--------------- 1 file changed, 191 insertions(+), 215 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index f25a0c41..36fbf483 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -3,19 +3,19 @@ import time from logging import info, debug import logging -from struct import unpack_from,calcsize +from struct import unpack_from, calcsize import threading logging.basicConfig(level=logging.INFO) # zero means parse all incoming data (every second) CELL_INFO_REFRESH_S = 0 -DEVICE_INFO_REFRESH_S = 60*60*5 #every 5 Hours -CHAR_HANDLE="0000ffe1-0000-1000-8000-00805f9b34fb" +DEVICE_INFO_REFRESH_S = 60*60*5 # every 5 Hours +CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" -COMMAND_CELL_INFO = 0x96; -COMMAND_DEVICE_INFO = 0x97; +COMMAND_CELL_INFO = 0x96 +COMMAND_DEVICE_INFO = 0x97 FRAME_VERSION_JK04 = 0x01 FRAME_VERSION_JK02 = 0x02 @@ -23,84 +23,73 @@ PROTOCOL_VERSION_JK02 = 0x02 -protocol_version=PROTOCOL_VERSION_JK02 +protocol_version = PROTOCOL_VERSION_JK02 -MIN_RESPONSE_SIZE = 300; -MAX_RESPONSE_SIZE = 320; +MIN_RESPONSE_SIZE = 300 +MAX_RESPONSE_SIZE = 320 TRANSLATE_DEVICE_INFO = [ - [["device_info","hw_rev"],22,"8s"], - [["device_info","sw_rev"],30,"8s"], - [["device_info","uptime"],38," CELL_INFO_REFRESH_S: - self.last_cell_info=time.time() + if (CELL_INFO_REFRESH_S == 0 or + time.time() - self.last_cell_info > CELL_INFO_REFRESH_S): + self.last_cell_info = time.time() info("processing frame with battery cell info") if protocol_version == PROTOCOL_VERSION_JK02: self.decode_cellinfo_jk02() - self.bms_status["last_update"]=time.time() - #overriding special values - ## power is calculated from voltage x current as register 122 contains unsigned power-value - self.decode_cellinfo_jk02() - self.bms_status["last_update"]=time.time() - #overriding special values - ## power is calculated from voltage x current as register 122 contains unsigned power-value - self.bms_status["cell_info"]["power"]=self.bms_status["cell_info"]["current"]*self.bms_status["cell_info"]["total_voltage"] - if self.waiting_for_response=="cell_info": - self.waiting_for_response="" + self.bms_status["last_update"] = time.time() + # power is calculated from voltage x current as + # register 122 contains unsigned power-value + self.bms_status["cell_info"]["power"] = ( + self.bms_status["cell_info"]["current"] * + self.bms_status["cell_info"]["total_voltage"]) + if self.waiting_for_response == "cell_info": + self.waiting_for_response = "" elif info_type == 0x03: - info("processing frame with device info") if protocol_version == PROTOCOL_VERSION_JK02: self.decode_device_info_jk02() - self.bms_status["last_update"]=time.time() + self.bms_status["last_update"] = time.time() else: return - if self.waiting_for_response=="device_info": - self.waiting_for_response="" + if self.waiting_for_response == "device_info": + self.waiting_for_response = "" - def assemble_frame(self, data:bytearray): - #global frame_buffer + def assemble_frame(self, data: bytearray): if len(self.frame_buffer) > MAX_RESPONSE_SIZE: info("data dropped because it alone was longer than max frame length") - self.frame_buffer=[] - - if data[0] == 0x55 and data[1] == 0xAA and data[2] == 0xEB and data[3] == 0x90: - #beginning of new frame, clear buffer - self.frame_buffer=[] - + self.frame_buffer = [] + + if (data[0] == 0x55 and data[1] == 0xAA and + data[2] == 0xEB and data[3] == 0x90): + # beginning of new frame, clear buffer + self.frame_buffer = [] + self.frame_buffer.extend(data) - + if len(self.frame_buffer) >= MIN_RESPONSE_SIZE: - #check crc; always at position 300, independent of actual frame-lentgh, so crc up to 299 - ccrc=self.crc(self.frame_buffer,300-1) - rcrc=self.frame_buffer[300-1] - debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") + # check crc; always at position 300, independent of + # actual frame-lentgh, so crc up to 299 + ccrc = self.crc(self.frame_buffer, 300 - 1) + rcrc = self.frame_buffer[300-1] + debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") if ccrc == rcrc: - debug("great success! frame complete and sane, lets go decoding") + debug("great success! frame complete and sane, lets decode") self.decode() - self.frame_buffer=[] + self.frame_buffer = [] def ncallback(self, sender: int, data: bytearray): debug(f"------> NEW PACKAGE!laenge: {len(data)}") self.assemble_frame(data) - def crc(self, arr:bytearray, length: int) -> int: - crc = 0; + def crc(self, arr: bytearray, length: int) -> int: + crc = 0 for a in arr[:length]: - crc = crc + a; + crc = crc + a return crc.to_bytes(2, 'little')[0] - async def write_register(self, address, vals :bytearray,length:int, bleakC:BleakClient): + async def write_register(self, address, vals: bytearray, + length: int, bleakC: BleakClient): frame = bytearray(20) - frame[0] = 0xAA #start sequence - frame[1] = 0x55 #start sequence - frame[2] = 0x90 #start sequence - frame[3] = 0xEB #start sequence - frame[4] = address #holding register - frame[5] = length #size of the value in byte + frame[0] = 0xAA # start sequence + frame[1] = 0x55 # start sequence + frame[2] = 0x90 # start sequence + frame[3] = 0xEB # start sequence + frame[4] = address # holding register + frame[5] = length # size of the value in byte frame[6] = vals[0] frame[7] = vals[1] frame[8] = vals[2] @@ -285,98 +260,99 @@ async def write_register(self, address, vals :bytearray,length:int, bleakC:Bleak frame[16] = 0x00 frame[17] = 0x00 frame[18] = 0x00 - - frame[19] = self.crc(frame,len(frame)-1) - debug( "Write register: ",frame ) - await bleakC.write_gatt_char(CHAR_HANDLE,frame, False) - - - - async def request_bt(self, rtype :str, client): - #global waiting_for_response - + frame[19] = self.crc(frame, len(frame)-1) + debug("Write register: ", frame) + await bleakC.write_gatt_char(CHAR_HANDLE, frame, False) + async def request_bt(self, rtype: str, client): timeout = time.time() - while self.waiting_for_response!="" and time.time()-timeout < 10 : + while self.waiting_for_response != "" and time.time()-timeout < 10: await asyncio.sleep(1) print(self.waiting_for_response) - - if rtype=="cell_info": - cmd=COMMAND_CELL_INFO - self.waiting_for_response="cell_info" - elif rtype=="device_info": - cmd=COMMAND_DEVICE_INFO - self.waiting_for_response="device_info" + if rtype == "cell_info": + cmd = COMMAND_CELL_INFO + self.waiting_for_response = "cell_info" + elif rtype == "device_info": + cmd = COMMAND_DEVICE_INFO + self.waiting_for_response = "device_info" else: return - - await self.write_register(cmd,b'\0\0\0\0',0x00,client) - + + await self.write_register(cmd, b'\0\0\0\0', 0x00, client) + def get_status(self): - if "settings" in self.bms_status and "cell_info" in self.bms_status: + if "settings" in self.bms_status and "cell_info" in self.bms_status: return self.bms_status else: return None def connect_and_scrape(self): asyncio.run(self.asy_connect_and_scrape()) - + async def asy_connect_and_scrape(self): - print("connect and scrape on address: "+self.address) + print("connect and scrape on address: " + self.address) self.run = True - while self.run and self.main_thread.is_alive(): #autoreconnect + while self.run and self.main_thread.is_alive(): # autoreconnect client = BleakClient(self.address) print("btloop") try: await client.connect() - self.bms_status["model_nbr"]= (await client.read_gatt_char(MODEL_NBR_UUID)).decode("utf-8") - + self.bms_status["model_nbr"] = ( + await client.read_gatt_char(MODEL_NBR_UUID)).decode("utf-8") + await client.start_notify(CHAR_HANDLE, self.ncallback) await self.request_bt("device_info", client) - + await self.request_bt("cell_info", client) - # await self.enable_charging(client) - last_dev_info=time.time() - while client.is_connected and self.run and self.main_thread.is_alive(): - if time.time()-last_dev_info>DEVICE_INFO_REFRESH_S: - last_dev_info=time.time() + # await self.enable_charging(client) + last_dev_info = time.time() + while (client.is_connected and + self.run and self.main_thread.is_alive()): + if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: + last_dev_info = time.time() await self.request_bt("device_info", client) await asyncio.sleep(0.01) except Exception as e: info("error while connecting to bt: " + str(e)) - self.run=False + self.run = False finally: await client.disconnect() - + print("Exiting bt-loop") - - def start_scraping(self): - self.main_thread=threading.current_thread() + def start_scraping(self): + self.main_thread = threading.current_thread() if self.is_running(): return self.bt_thread.start() - info("scraping thread started -> main thread id: "+str(self.main_thread.ident)+" scraping thread: "+str(self.bt_thread.ident)) + info("scraping thread started -> main thread id: " + + str(self.main_thread.ident) + + " scraping thread: " + + str(self.bt_thread.ident)) def stop_scraping(self): - self.run=False - while is_running(): + self.run = False + while self.is_running(): time.sleep(0.1) - + def is_running(self): return self.bt_thread.is_alive() - async def enable_charging(self,c): - # these are the registers for the control-buttons; data is 01 00 00 00 for on 00 00 00 00 for off; the following bytes up to 19 are unclear and changing dynamically -> auth-mechanism? - await self.write_register(0x1d,b'\x01\x00\x00\x00',4,c) - await self.write_register(0x1e,b'\x01\x00\x00\x00',4,c) - await self.write_register(0x1f,b'\x01\x00\x00\x00',4,c) - await self.write_register(0x40,b'\x01\x00\x00\x00',4,c) + async def enable_charging(self, c): + # these are the registers for the control-buttons: + # data is 01 00 00 00 for on 00 00 00 00 for off; + # the following bytes up to 19 are unclear and changing + # dynamically -> auth-mechanism? + await self.write_register(0x1d, b'\x01\x00\x00\x00', 4, c) + await self.write_register(0x1e, b'\x01\x00\x00\x00', 4, c) + await self.write_register(0x1f, b'\x01\x00\x00\x00', 4, c) + await self.write_register(0x40, b'\x01\x00\x00\x00', 4, c) + if __name__ == "__main__": - jk = JkBmsBle("C8:47:8C:E4:54:0E") + jk = JkBmsBle("C8:47:8C:E4:54:0E") info("sss") jk.start_scraping() while True: From 68b26a348b356f7300cde25cdb28e9fdf69ef004 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 16:31:35 +0000 Subject: [PATCH 009/209] satisfy lint --- etc/dbus-serialbattery/jkbms_brn.py | 107 +++++++++++++++------------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 36fbf483..24aa6b97 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -5,6 +5,7 @@ import logging from struct import unpack_from, calcsize import threading + logging.basicConfig(level=logging.INFO) @@ -30,12 +31,11 @@ MAX_RESPONSE_SIZE = 320 TRANSLATE_DEVICE_INFO = [ - [["device_info", "hw_rev"], 22, "8s"], - [["device_info", "sw_rev"], 30, "8s"], - [["device_info", "uptime"], 38, " CELL_INFO_REFRESH_S): + if ( + CELL_INFO_REFRESH_S == 0 or + time.time() - self.last_cell_info > CELL_INFO_REFRESH_S + ): self.last_cell_info = time.time() info("processing frame with battery cell info") if protocol_version == PROTOCOL_VERSION_JK02: @@ -190,8 +199,9 @@ def decode(self): # power is calculated from voltage x current as # register 122 contains unsigned power-value self.bms_status["cell_info"]["power"] = ( - self.bms_status["cell_info"]["current"] * - self.bms_status["cell_info"]["total_voltage"]) + self.bms_status["cell_info"]["current"] + * self.bms_status["cell_info"]["total_voltage"] + ) if self.waiting_for_response == "cell_info": self.waiting_for_response = "" @@ -210,8 +220,7 @@ def assemble_frame(self, data: bytearray): info("data dropped because it alone was longer than max frame length") self.frame_buffer = [] - if (data[0] == 0x55 and data[1] == 0xAA and - data[2] == 0xEB and data[3] == 0x90): + if data[0] == 0x55 and data[1] == 0xAA and data[2] == 0xEB and data[3] == 0x90: # beginning of new frame, clear buffer self.frame_buffer = [] @@ -221,7 +230,7 @@ def assemble_frame(self, data: bytearray): # check crc; always at position 300, independent of # actual frame-lentgh, so crc up to 299 ccrc = self.crc(self.frame_buffer, 300 - 1) - rcrc = self.frame_buffer[300-1] + rcrc = self.frame_buffer[300 - 1] debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") if ccrc == rcrc: debug("great success! frame complete and sane, lets decode") @@ -236,17 +245,18 @@ def crc(self, arr: bytearray, length: int) -> int: crc = 0 for a in arr[:length]: crc = crc + a - return crc.to_bytes(2, 'little')[0] + return crc.to_bytes(2, "little")[0] - async def write_register(self, address, vals: bytearray, - length: int, bleakC: BleakClient): + async def write_register( + self, address, vals: bytearray, length: int, bleakC: BleakClient + ): frame = bytearray(20) - frame[0] = 0xAA # start sequence - frame[1] = 0x55 # start sequence - frame[2] = 0x90 # start sequence - frame[3] = 0xEB # start sequence - frame[4] = address # holding register - frame[5] = length # size of the value in byte + frame[0] = 0xAA # start sequence + frame[1] = 0x55 # start sequence + frame[2] = 0x90 # start sequence + frame[3] = 0xEB # start sequence + frame[4] = address # holding register + frame[5] = length # size of the value in byte frame[6] = vals[0] frame[7] = vals[1] frame[8] = vals[2] @@ -260,7 +270,7 @@ async def write_register(self, address, vals: bytearray, frame[16] = 0x00 frame[17] = 0x00 frame[18] = 0x00 - frame[19] = self.crc(frame, len(frame)-1) + frame[19] = self.crc(frame, len(frame) - 1) debug("Write register: ", frame) await bleakC.write_gatt_char(CHAR_HANDLE, frame, False) @@ -280,7 +290,7 @@ async def request_bt(self, rtype: str, client): else: return - await self.write_register(cmd, b'\0\0\0\0', 0x00, client) + await self.write_register(cmd, b"\0\0\0\0", 0x00, client) def get_status(self): if "settings" in self.bms_status and "cell_info" in self.bms_status: @@ -300,7 +310,8 @@ async def asy_connect_and_scrape(self): try: await client.connect() self.bms_status["model_nbr"] = ( - await client.read_gatt_char(MODEL_NBR_UUID)).decode("utf-8") + await client.read_gatt_char(MODEL_NBR_UUID) + ).decode("utf-8") await client.start_notify(CHAR_HANDLE, self.ncallback) await self.request_bt("device_info", client) @@ -308,8 +319,7 @@ async def asy_connect_and_scrape(self): await self.request_bt("cell_info", client) # await self.enable_charging(client) last_dev_info = time.time() - while (client.is_connected and - self.run and self.main_thread.is_alive()): + while client.is_connected and self.run and self.main_thread.is_alive(): if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: last_dev_info = time.time() await self.request_bt("device_info", client) @@ -327,10 +337,11 @@ def start_scraping(self): if self.is_running(): return self.bt_thread.start() - info("scraping thread started -> main thread id: " + - str(self.main_thread.ident) + - " scraping thread: " + - str(self.bt_thread.ident)) + info("scraping thread started -> main thread id: " + + str(self.main_thread.ident) + + " scraping thread: " + + str(self.bt_thread.ident) + ) def stop_scraping(self): self.run = False @@ -345,10 +356,10 @@ async def enable_charging(self, c): # data is 01 00 00 00 for on 00 00 00 00 for off; # the following bytes up to 19 are unclear and changing # dynamically -> auth-mechanism? - await self.write_register(0x1d, b'\x01\x00\x00\x00', 4, c) - await self.write_register(0x1e, b'\x01\x00\x00\x00', 4, c) - await self.write_register(0x1f, b'\x01\x00\x00\x00', 4, c) - await self.write_register(0x40, b'\x01\x00\x00\x00', 4, c) + await self.write_register(0x1D, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x1E, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x1F, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) if __name__ == "__main__": From 1f9eb87e2b5b77c003d360bc6ffa23ef69931aba Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 16:45:12 +0000 Subject: [PATCH 010/209] lint #2 --- etc/dbus-serialbattery/jkbms_ble.py | 87 ++++++++++++++++++----------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index e54cf924..a5743e59 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -4,13 +4,14 @@ from struct import * from jkbms_brn import JkBmsBle from bleak import BleakScanner -import asyncio +import asyncio import time + class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" - def __init__(self, port,baud, address): - super(Jkbms_Ble, self).__init__("zero",baud) + def __init__(self, port, baud, address): + super(Jkbms_Ble, self).__init__("zero", baud) self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) @@ -20,22 +21,26 @@ def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. # The result or call should be unique to this BMS. Battery name or version, etc. # Return True if success, False for failure - + # check if device with given mac is found, otherwise abort - logger.error("test of jkbmsble") - loop = asyncio.get_event_loop() - t = loop.create_task(BleakScanner.discover()) - devices = loop.run_until_complete(t) + logger.info("test of jkbmsble") + try: + loop = asyncio.get_event_loop() + t = loop.create_task(BleakScanner.discover()) + devices = loop.run_until_complete(t) + except BleakErrori as e: + logger.error(str(e)) + return False - found=False + found = False for d in devices: if d.address == self.jk.address: - found=True + found = True if not found: return False - #device was found, presumeably a jkbms so start scraping + # device was found, presumeably a jkbms so start scraping self.jk.start_scraping() tries = 1 @@ -44,13 +49,13 @@ def test_connection(self): tries += 1 # load initial data, from here on get_status has valid values to be served to the dbus - status=self.jk.get_status() + status = self.jk.get_status() if status == None: return False - + if not status["device_info"]["vendor_id"].startswith("JK-"): return False - + logger.info("JK BMS found!") return True @@ -58,8 +63,8 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - st=self.jk.get_status()["settings"] - self.cell_count=st["cell_count"] + st = self.jk.get_status()["settings"] + self.cell_count = st["cell_count"] self.max_battery_charge_current = st["max_charge_current"] self.max_battery_discharge_current = st["max_discharge_current"] self.max_battery_voltage = st["cell_ovp"] * self.cell_count @@ -68,8 +73,14 @@ def get_settings(self): for c in range(self.cell_count): self.cells.append(Cell(False)) - self.hardware_version = "JKBMS "+ self.jk.get_status()["device_info"]["hw_rev"]+" " + str(self.cell_count) + " cells" - logger.info("BAT: "+self.hardware_version) + self.hardware_version = ( + "JKBMS " + + self.jk.get_status()["device_info"]["hw_rev"] + + " " + + str(self.cell_count) + + " cells" + ) + logger.info("BAT: " + self.hardware_version) return True def refresh_data(self): @@ -77,26 +88,26 @@ def refresh_data(self): # This will be called for every iteration (1 second) # Return True if success, False for failure - #result = self.read_soc_data() - #TODO: check for errors - st=self.jk.get_status() + # result = self.read_soc_data() + # TODO: check for errors + st = self.jk.get_status() if st == None: return False if time.time() - st["last_update"] > 30: - #if data not updated for more than 30s, sth is wrong, then fail + # if data not updated for more than 30s, sth is wrong, then fail return False for c in range(self.cell_count): - self.cells[c].voltage=st["cell_info"]["voltages"][c] - + self.cells[c].voltage = st["cell_info"]["voltages"][c] + self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.current=st["cell_info"]["current"] - self.voltage=st["cell_info"]["total_voltage"] + self.current = st["cell_info"]["current"] + self.voltage = st["cell_info"]["total_voltage"] - self.soc=st["cell_info"]["battery_soc"] - self.cycles=st["cell_info"]["cycle_count"] - self.capacity=st["cell_info"]["capacity_nominal"] + self.soc = st["cell_info"]["battery_soc"] + self.cycles = st["cell_info"]["cycle_count"] + self.capacity = st["cell_info"]["capacity_nominal"] #protection bits #self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 @@ -105,10 +116,20 @@ def refresh_data(self): self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 - self.protection.current_over = 2 if (st["warnings"]["charge_overcurrent"] or st["warnings"]["discharge_overcurrent"]) else 0 - - self.protection.set_IC_inspection = 2 if st["cell_info"]["temperature_mos"] > 80 else 0 + self.protection.current_over = ( + 2 + if ( + st["warnings"]["charge_overcurrent"] + or st["warnings"]["discharge_overcurrent"] + ) + else 0 + ) + self.protection.set_IC_inspection = ( + 2 if st["cell_info"]["temperature_mos"] > 80 else 0 + ) self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0 self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0 - self.protection.temp_high_discharge = 2 if st["warnings"]["discharge_overtemp"] else 0 + self.protection.temp_high_discharge = ( + 2 if st["warnings"]["discharge_overtemp"] else 0 + ) return True From 973718bfadfd41caee3d13a08ddcbd012334fc98 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 16:54:48 +0000 Subject: [PATCH 011/209] lint #3 --- etc/dbus-serialbattery/jkbms_ble.py | 27 ++++++++++++++------------- etc/dbus-serialbattery/jkbms_brn.py | 25 +++++++++++++------------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index a5743e59..bf8318fb 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -10,6 +10,7 @@ class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" + def __init__(self, port, baud, address): super(Jkbms_Ble, self).__init__("zero", baud) self.type = self.BATTERYTYPE @@ -41,20 +42,20 @@ def test_connection(self): return False # device was found, presumeably a jkbms so start scraping - self.jk.start_scraping() + self.jk.start_scraping() tries = 1 - + while self.jk.get_status() == None and tries < 20: time.sleep(0.5) tries += 1 - # load initial data, from here on get_status has valid values to be served to the dbus + # load initial data, from here on get_status has valid values to be served to the dbus status = self.jk.get_status() if status == None: return False if not status["device_info"]["vendor_id"].startswith("JK-"): - return False + return False logger.info("JK BMS found!") return True @@ -87,7 +88,7 @@ def refresh_data(self): # call all functions that will refresh the battery data. # This will be called for every iteration (1 second) # Return True if success, False for failure - + # result = self.read_soc_data() # TODO: check for errors st = self.jk.get_status() @@ -96,7 +97,7 @@ def refresh_data(self): if time.time() - st["last_update"] > 30: # if data not updated for more than 30s, sth is wrong, then fail return False - + for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] @@ -109,19 +110,19 @@ def refresh_data(self): self.cycles = st["cell_info"]["cycle_count"] self.capacity = st["cell_info"]["capacity_nominal"] - #protection bits - #self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 - #self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 + # protection bits + # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 + # self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 - self.protection.current_over = ( - 2 + self.protection.current_over = ( + 2 if ( - st["warnings"]["charge_overcurrent"] + st["warnings"]["charge_overcurrent"] or st["warnings"]["discharge_overcurrent"] - ) + ) else 0 ) self.protection.set_IC_inspection = ( diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 24aa6b97..bca4d529 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -11,7 +11,7 @@ # zero means parse all incoming data (every second) CELL_INFO_REFRESH_S = 0 -DEVICE_INFO_REFRESH_S = 60*60*5 # every 5 Hours +DEVICE_INFO_REFRESH_S = 60 * 60 * 5 # every 5 Hours CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" @@ -104,8 +104,8 @@ def translate(self, fb, translation, o, i=0): if i == len(translation[0]) - 1: # keep things universal by using an n=1 list kees = ( - range(0, translation[0][i]) - if isinstance(translation[0][i], int) + range(0, translation[0][i]) + if isinstance(translation[0][i], int) else [translation[0][i]] ) i = 0 @@ -114,7 +114,7 @@ def translate(self, fb, translation, o, i=0): # handle raw bytes without unpack_from; # 3. param gives no format but number of bytes val = bytearray( - fb[translation[1] + i:translation[1] + i + translation[2]] + fb[translation[1] + i : translation[1] + i + translation[2]] ) i += translation[2] else: @@ -130,8 +130,8 @@ def translate(self, fb, translation, o, i=0): o[j] = val else: if translation[0][i] not in o: - if len(translation[0]) == i+2 and isinstance( - translation[0][i+1], int + if len(translation[0]) == i + 2 and isinstance( + translation[0][i + 1], int ): o[translation[0][i]] = [None] * translation[0][i + 1] else: @@ -188,8 +188,8 @@ def decode(self): elif info_type == 0x02: if ( - CELL_INFO_REFRESH_S == 0 or - time.time() - self.last_cell_info > CELL_INFO_REFRESH_S + CELL_INFO_REFRESH_S == 0 + or time.time() - self.last_cell_info > CELL_INFO_REFRESH_S ): self.last_cell_info = time.time() info("processing frame with battery cell info") @@ -199,8 +199,8 @@ def decode(self): # power is calculated from voltage x current as # register 122 contains unsigned power-value self.bms_status["cell_info"]["power"] = ( - self.bms_status["cell_info"]["current"] - * self.bms_status["cell_info"]["total_voltage"] + self.bms_status["cell_info"]["current"] + * self.bms_status["cell_info"]["total_voltage"] ) if self.waiting_for_response == "cell_info": self.waiting_for_response = "" @@ -277,7 +277,7 @@ async def write_register( async def request_bt(self, rtype: str, client): timeout = time.time() - while self.waiting_for_response != "" and time.time()-timeout < 10: + while self.waiting_for_response != "" and time.time() - timeout < 10: await asyncio.sleep(1) print(self.waiting_for_response) @@ -337,7 +337,8 @@ def start_scraping(self): if self.is_running(): return self.bt_thread.start() - info("scraping thread started -> main thread id: " + info( + "scraping thread started -> main thread id: " + str(self.main_thread.ident) + " scraping thread: " + str(self.bt_thread.ident) From c0ea4a069aaf68821a23f5331f5c13c355c9c06d Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 17:01:32 +0000 Subject: [PATCH 012/209] lint again --- etc/dbus-serialbattery/jkbms_ble.py | 4 ++-- etc/dbus-serialbattery/jkbms_brn.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index bf8318fb..995efb25 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -113,10 +113,10 @@ def refresh_data(self): # protection bits # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 # self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 - + self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 - + self.protection.current_over = ( 2 if ( diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index bca4d529..22db5f10 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -339,9 +339,9 @@ def start_scraping(self): self.bt_thread.start() info( "scraping thread started -> main thread id: " - + str(self.main_thread.ident) - + " scraping thread: " - + str(self.bt_thread.ident) + + str(self.main_thread.ident) + + " scraping thread: " + + str(self.bt_thread.ident) ) def stop_scraping(self): From 39345dad4e503cc0006211bdb741bb54fc70e705 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 17:12:44 +0000 Subject: [PATCH 013/209] cleanup imports & None-check --- etc/dbus-serialbattery/jkbms_ble.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 995efb25..7a55af9c 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery, Cell +from utils import logger +# from struct import * from jkbms_brn import JkBmsBle from bleak import BleakScanner import asyncio @@ -30,7 +30,7 @@ def test_connection(self): loop = asyncio.get_event_loop() t = loop.create_task(BleakScanner.discover()) devices = loop.run_until_complete(t) - except BleakErrori as e: + except BleakError as e: logger.error(str(e)) return False @@ -45,13 +45,13 @@ def test_connection(self): self.jk.start_scraping() tries = 1 - while self.jk.get_status() == None and tries < 20: + while self.jk.get_status() is None and tries < 20: time.sleep(0.5) tries += 1 # load initial data, from here on get_status has valid values to be served to the dbus status = self.jk.get_status() - if status == None: + if status is None: return False if not status["device_info"]["vendor_id"].startswith("JK-"): @@ -92,7 +92,7 @@ def refresh_data(self): # result = self.read_soc_data() # TODO: check for errors st = self.jk.get_status() - if st == None: + if st is None: return False if time.time() - st["last_update"] > 30: # if data not updated for more than 30s, sth is wrong, then fail From 672192f9ba94c7a24c62d61fdb47f399ceb51d89 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 17:17:48 +0000 Subject: [PATCH 014/209] lint --- etc/dbus-serialbattery/jkbms_ble.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 7a55af9c..df596d25 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from battery import Battery, Cell from utils import logger -# from struct import * from jkbms_brn import JkBmsBle from bleak import BleakScanner import asyncio From 6732d417b0e409cc81defc4e1c7dda950dcd5669 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 7 Jan 2023 17:22:27 +0000 Subject: [PATCH 015/209] missing import --- etc/dbus-serialbattery/jkbms_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index df596d25..d6fb4fa3 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -2,7 +2,7 @@ from battery import Battery, Cell from utils import logger from jkbms_brn import JkBmsBle -from bleak import BleakScanner +from bleak import BleakScanner, BleakError import asyncio import time From bdc777c5cfe61d3edac91662ba4c3a5f7cf8d042 Mon Sep 17 00:00:00 2001 From: Eike Date: Mon, 9 Jan 2023 20:15:27 +0100 Subject: [PATCH 016/209] Bluetooth-Support for JK BMS (#372) * jkbms ble integration * implemented most warnings except undercurrent(?), imbalance * getting things together, removed most obvious mistakes; connection test is executed, further work required * give the bluetooth-jkbms a dummy-serialport that will fail to open, so that the bt-connection can be tested * added installation dependencies to installlocal; added error-handling if no bt-device is available during test_connection * cleanup imports & None-check Co-authored-by: Eike Baran --- buildfiles.lst | 4 +- etc/dbus-serialbattery/dbus-serialbattery.py | 1 + etc/dbus-serialbattery/installlocal.sh | 7 +- etc/dbus-serialbattery/jkbms_ble.py | 135 +++++++ etc/dbus-serialbattery/jkbms_brn.py | 373 +++++++++++++++++++ etc/dbus-serialbattery/utils.py | 1 + 6 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 etc/dbus-serialbattery/jkbms_ble.py create mode 100644 etc/dbus-serialbattery/jkbms_brn.py diff --git a/buildfiles.lst b/buildfiles.lst index 9efc81ca..fe7809b0 100644 --- a/buildfiles.lst +++ b/buildfiles.lst @@ -24,7 +24,9 @@ etc/dbus-serialbattery/ant.py etc/dbus-serialbattery/util_max17853.py etc/dbus-serialbattery/mnb.py etc/dbus-serialbattery/jkbms.py +etc/dbus-serialbattery/jkbms_ble.py +etc/dbus-serialbattery/jkbms_brn.py etc/dbus-serialbattery/sinowealth.py etc/dbus-serialbattery/renogy.py etc/dbus-serialbattery/ecs.py -etc/dbus-serialbattery/lifepower.py \ No newline at end of file +etc/dbus-serialbattery/lifepower.py diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 932236ad..d74bbd8b 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -21,6 +21,7 @@ from daly import Daly from ant import Ant from jkbms import Jkbms +from jkbms_ble import Jkbms_Ble from sinowealth import Sinowealth from renogy import Renogy from ecs import Ecs diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index b389ccd2..76b1d6b5 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,3 +1,8 @@ #!/bin/sh +opkg update +opkg install python3-misc python3-pip +pip3 install bleak tar -zxf ./venus-data.tar.gz -C /data -sh /data/etc/dbus-serialbattery/reinstalllocal.sh \ No newline at end of file +sh /data/etc/dbus-serialbattery/reinstalllocal.sh +echo "make sure to disable Settings/Bluetooth in the Remote-Console to prevent reconnects every minute. In case of crash after ~12-16 hours disable raspberry pi 3 internal bluetooth via dtoverlay and use an external usb bluetooth-dongle" + diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py new file mode 100644 index 00000000..d6fb4fa3 --- /dev/null +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +from battery import Battery, Cell +from utils import logger +from jkbms_brn import JkBmsBle +from bleak import BleakScanner, BleakError +import asyncio +import time + + +class Jkbms_Ble(Battery): + BATTERYTYPE = "Jkbms BLE" + + def __init__(self, port, baud, address): + super(Jkbms_Ble, self).__init__("zero", baud) + self.type = self.BATTERYTYPE + self.jk = JkBmsBle(address) + + logger.error("init of jkbmsble") + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + + # check if device with given mac is found, otherwise abort + + logger.info("test of jkbmsble") + try: + loop = asyncio.get_event_loop() + t = loop.create_task(BleakScanner.discover()) + devices = loop.run_until_complete(t) + except BleakError as e: + logger.error(str(e)) + return False + + found = False + for d in devices: + if d.address == self.jk.address: + found = True + if not found: + return False + + # device was found, presumeably a jkbms so start scraping + self.jk.start_scraping() + tries = 1 + + while self.jk.get_status() is None and tries < 20: + time.sleep(0.5) + tries += 1 + + # load initial data, from here on get_status has valid values to be served to the dbus + status = self.jk.get_status() + if status is None: + return False + + if not status["device_info"]["vendor_id"].startswith("JK-"): + return False + + logger.info("JK BMS found!") + return True + + def get_settings(self): + # After successful connection get_settings will be call to set up the battery. + # Set the current limits, populate cell count, etc + # Return True if success, False for failure + st = self.jk.get_status()["settings"] + self.cell_count = st["cell_count"] + self.max_battery_charge_current = st["max_charge_current"] + self.max_battery_discharge_current = st["max_discharge_current"] + self.max_battery_voltage = st["cell_ovp"] * self.cell_count + self.min_battery_voltage = st["cell_uvp"] * self.cell_count + + for c in range(self.cell_count): + self.cells.append(Cell(False)) + + self.hardware_version = ( + "JKBMS " + + self.jk.get_status()["device_info"]["hw_rev"] + + " " + + str(self.cell_count) + + " cells" + ) + logger.info("BAT: " + self.hardware_version) + return True + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (1 second) + # Return True if success, False for failure + + # result = self.read_soc_data() + # TODO: check for errors + st = self.jk.get_status() + if st is None: + return False + if time.time() - st["last_update"] > 30: + # if data not updated for more than 30s, sth is wrong, then fail + return False + + for c in range(self.cell_count): + self.cells[c].voltage = st["cell_info"]["voltages"][c] + + self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) + self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) + self.current = st["cell_info"]["current"] + self.voltage = st["cell_info"]["total_voltage"] + + self.soc = st["cell_info"]["battery_soc"] + self.cycles = st["cell_info"]["cycle_count"] + self.capacity = st["cell_info"]["capacity_nominal"] + + # protection bits + # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 + # self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 + + self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 + self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 + + self.protection.current_over = ( + 2 + if ( + st["warnings"]["charge_overcurrent"] + or st["warnings"]["discharge_overcurrent"] + ) + else 0 + ) + self.protection.set_IC_inspection = ( + 2 if st["cell_info"]["temperature_mos"] > 80 else 0 + ) + self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0 + self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0 + self.protection.temp_high_discharge = ( + 2 if st["warnings"]["discharge_overtemp"] else 0 + ) + return True diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py new file mode 100644 index 00000000..22db5f10 --- /dev/null +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -0,0 +1,373 @@ +import asyncio +from bleak import BleakScanner, BleakClient +import time +from logging import info, debug +import logging +from struct import unpack_from, calcsize +import threading + +logging.basicConfig(level=logging.INFO) + + +# zero means parse all incoming data (every second) +CELL_INFO_REFRESH_S = 0 +DEVICE_INFO_REFRESH_S = 60 * 60 * 5 # every 5 Hours +CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" +MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" + +COMMAND_CELL_INFO = 0x96 +COMMAND_DEVICE_INFO = 0x97 + +FRAME_VERSION_JK04 = 0x01 +FRAME_VERSION_JK02 = 0x02 +FRAME_VERSION_JK02_32S = 0x03 +PROTOCOL_VERSION_JK02 = 0x02 + + +protocol_version = PROTOCOL_VERSION_JK02 + + +MIN_RESPONSE_SIZE = 300 +MAX_RESPONSE_SIZE = 320 + +TRANSLATE_DEVICE_INFO = [ + [["device_info", "hw_rev"], 22, "8s"], + [["device_info", "sw_rev"], 30, "8s"], + [["device_info", "uptime"], 38, " CELL_INFO_REFRESH_S + ): + self.last_cell_info = time.time() + info("processing frame with battery cell info") + if protocol_version == PROTOCOL_VERSION_JK02: + self.decode_cellinfo_jk02() + self.bms_status["last_update"] = time.time() + # power is calculated from voltage x current as + # register 122 contains unsigned power-value + self.bms_status["cell_info"]["power"] = ( + self.bms_status["cell_info"]["current"] + * self.bms_status["cell_info"]["total_voltage"] + ) + if self.waiting_for_response == "cell_info": + self.waiting_for_response = "" + + elif info_type == 0x03: + info("processing frame with device info") + if protocol_version == PROTOCOL_VERSION_JK02: + self.decode_device_info_jk02() + self.bms_status["last_update"] = time.time() + else: + return + if self.waiting_for_response == "device_info": + self.waiting_for_response = "" + + def assemble_frame(self, data: bytearray): + if len(self.frame_buffer) > MAX_RESPONSE_SIZE: + info("data dropped because it alone was longer than max frame length") + self.frame_buffer = [] + + if data[0] == 0x55 and data[1] == 0xAA and data[2] == 0xEB and data[3] == 0x90: + # beginning of new frame, clear buffer + self.frame_buffer = [] + + self.frame_buffer.extend(data) + + if len(self.frame_buffer) >= MIN_RESPONSE_SIZE: + # check crc; always at position 300, independent of + # actual frame-lentgh, so crc up to 299 + ccrc = self.crc(self.frame_buffer, 300 - 1) + rcrc = self.frame_buffer[300 - 1] + debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") + if ccrc == rcrc: + debug("great success! frame complete and sane, lets decode") + self.decode() + self.frame_buffer = [] + + def ncallback(self, sender: int, data: bytearray): + debug(f"------> NEW PACKAGE!laenge: {len(data)}") + self.assemble_frame(data) + + def crc(self, arr: bytearray, length: int) -> int: + crc = 0 + for a in arr[:length]: + crc = crc + a + return crc.to_bytes(2, "little")[0] + + async def write_register( + self, address, vals: bytearray, length: int, bleakC: BleakClient + ): + frame = bytearray(20) + frame[0] = 0xAA # start sequence + frame[1] = 0x55 # start sequence + frame[2] = 0x90 # start sequence + frame[3] = 0xEB # start sequence + frame[4] = address # holding register + frame[5] = length # size of the value in byte + frame[6] = vals[0] + frame[7] = vals[1] + frame[8] = vals[2] + frame[9] = vals[3] + frame[10] = 0x00 + frame[11] = 0x00 + frame[12] = 0x00 + frame[13] = 0x00 + frame[14] = 0x00 + frame[15] = 0x00 + frame[16] = 0x00 + frame[17] = 0x00 + frame[18] = 0x00 + frame[19] = self.crc(frame, len(frame) - 1) + debug("Write register: ", frame) + await bleakC.write_gatt_char(CHAR_HANDLE, frame, False) + + async def request_bt(self, rtype: str, client): + timeout = time.time() + + while self.waiting_for_response != "" and time.time() - timeout < 10: + await asyncio.sleep(1) + print(self.waiting_for_response) + + if rtype == "cell_info": + cmd = COMMAND_CELL_INFO + self.waiting_for_response = "cell_info" + elif rtype == "device_info": + cmd = COMMAND_DEVICE_INFO + self.waiting_for_response = "device_info" + else: + return + + await self.write_register(cmd, b"\0\0\0\0", 0x00, client) + + def get_status(self): + if "settings" in self.bms_status and "cell_info" in self.bms_status: + return self.bms_status + else: + return None + + def connect_and_scrape(self): + asyncio.run(self.asy_connect_and_scrape()) + + async def asy_connect_and_scrape(self): + print("connect and scrape on address: " + self.address) + self.run = True + while self.run and self.main_thread.is_alive(): # autoreconnect + client = BleakClient(self.address) + print("btloop") + try: + await client.connect() + self.bms_status["model_nbr"] = ( + await client.read_gatt_char(MODEL_NBR_UUID) + ).decode("utf-8") + + await client.start_notify(CHAR_HANDLE, self.ncallback) + await self.request_bt("device_info", client) + + await self.request_bt("cell_info", client) + # await self.enable_charging(client) + last_dev_info = time.time() + while client.is_connected and self.run and self.main_thread.is_alive(): + if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: + last_dev_info = time.time() + await self.request_bt("device_info", client) + await asyncio.sleep(0.01) + except Exception as e: + info("error while connecting to bt: " + str(e)) + self.run = False + finally: + await client.disconnect() + + print("Exiting bt-loop") + + def start_scraping(self): + self.main_thread = threading.current_thread() + if self.is_running(): + return + self.bt_thread.start() + info( + "scraping thread started -> main thread id: " + + str(self.main_thread.ident) + + " scraping thread: " + + str(self.bt_thread.ident) + ) + + def stop_scraping(self): + self.run = False + while self.is_running(): + time.sleep(0.1) + + def is_running(self): + return self.bt_thread.is_alive() + + async def enable_charging(self, c): + # these are the registers for the control-buttons: + # data is 01 00 00 00 for on 00 00 00 00 for off; + # the following bytes up to 19 are unclear and changing + # dynamically -> auth-mechanism? + await self.write_register(0x1D, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x1E, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x1F, b"\x01\x00\x00\x00", 4, c) + await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) + + +if __name__ == "__main__": + jk = JkBmsBle("C8:47:8C:E4:54:0E") + info("sss") + jk.start_scraping() + while True: + print("asdf") + print(jk.get_status()) + time.sleep(5) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 6c61a7c0..f0c61eba 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -18,6 +18,7 @@ {"bms": "Daly", "address": b"\x40"}, {"bms": "Daly", "address": b"\x80"}, {"bms": "Jkbms", "baud": 115200}, + {"bms": "Jkbms_Ble", "address": "C8:47:8C:E4:54:0E"}, # {"bms" : "Sinowealth"}, {"bms": "Lifepower"}, {"bms": "Renogy", "address": b"\x30"}, From 9774633cebfa3d9fb35af4f6bf02a6942e728afd Mon Sep 17 00:00:00 2001 From: Eike Date: Sat, 14 Jan 2023 14:57:55 +0100 Subject: [PATCH 017/209] Update jkbms_brn.py (#411) removed an accidently added letter "i" which breaks function --- etc/dbus-serialbattery/jkbms_brn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 22db5f10..5a3bea37 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -87,7 +87,7 @@ class JkBmsBle: frame_buffer = bytearray() bms_status = {} - waiting_for_responsei = "" + waiting_for_response = "" last_cell_info = 0 def __init__(self, addr): From 7052a7be4255bdd78d6bcee2e99432ad02536c27 Mon Sep 17 00:00:00 2001 From: Louisvdw <26077665+Louisvdw@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:40:03 +0200 Subject: [PATCH 018/209] update version --- etc/dbus-serialbattery/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index f0c61eba..69fecf54 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -29,7 +29,7 @@ # Constants - Need to dynamically get them in future DRIVER_VERSION = 0.14 -DRIVER_SUBVERSION = ".3" +DRIVER_SUBVERSION = ".3ble" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 183910c12208e9828cf15c2e5efbe3441b813b46 Mon Sep 17 00:00:00 2001 From: Louisvdw <26077665+Louisvdw@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:41:47 +0200 Subject: [PATCH 019/209] update version --- etc/dbus-serialbattery/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 69fecf54..bb6eaea9 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -29,7 +29,7 @@ # Constants - Need to dynamically get them in future DRIVER_VERSION = 0.14 -DRIVER_SUBVERSION = ".3ble" +DRIVER_SUBVERSION = "b3ble" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 60a4cf4cbc9f03d887e3f5bb8daf157a73a9b843 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Fri, 20 Jan 2023 23:43:27 +0100 Subject: [PATCH 020/209] workaround for crash of bluetooth-stack --- etc/dbus-serialbattery/jkbms_ble.py | 51 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index d6fb4fa3..67636bea 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -5,7 +5,7 @@ from bleak import BleakScanner, BleakError import asyncio import time - +import os class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" @@ -25,20 +25,26 @@ def test_connection(self): # check if device with given mac is found, otherwise abort logger.info("test of jkbmsble") - try: - loop = asyncio.get_event_loop() - t = loop.create_task(BleakScanner.discover()) - devices = loop.run_until_complete(t) - except BleakError as e: - logger.error(str(e)) - return False - - found = False - for d in devices: - if d.address == self.jk.address: - found = True - if not found: - return False + tries = 0 + while True: + try: + loop = asyncio.get_event_loop() + t = loop.create_task(BleakScanner.find_device_by_address(self.jk.address)) + device = loop.run_until_complete(t) + + if device is None: + if tries > 2: + return False + else: + # device found, exit loop and continue test + break + except BleakError as e: + if tries > 2: + return False + # recover from error if tries left + logger.error(str(e)) + self.reset_bluetooth() + tries += 1 # device was found, presumeably a jkbms so start scraping self.jk.start_scraping() @@ -95,6 +101,12 @@ def refresh_data(self): return False if time.time() - st["last_update"] > 30: # if data not updated for more than 30s, sth is wrong, then fail + if self.jk.is_running(): + # if the thread is still alive but data too old there is sth + # wrong with the bt-connection; restart whole stack + self.reset_connection() + self.jk.start_scraping() + time.sleep(2) return False for c in range(self.cell_count): @@ -133,3 +145,12 @@ def refresh_data(self): 2 if st["warnings"]["discharge_overtemp"] else 0 ) return True + + def reset_bluetooth(self): + if self.jk.is_running(): + self.jk.stop_scraping() + os.system("kill -9 $(pidof bluetoothd)") + time.sleep(1) + os.system("rfkill block bluetooth") + os.system("rfkill unblock bluetooth") + os.system("/etc/init.d/bluetooth start") From 4af0a5ad01761ad7af2bc00149337810de91fd51 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Fri, 20 Jan 2023 23:49:43 +0100 Subject: [PATCH 021/209] lint --- etc/dbus-serialbattery/jkbms_ble.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 67636bea..91019a3d 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -7,6 +7,7 @@ import time import os + class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" @@ -29,9 +30,11 @@ def test_connection(self): while True: try: loop = asyncio.get_event_loop() - t = loop.create_task(BleakScanner.find_device_by_address(self.jk.address)) + t = loop.create_task( + BleakScanner.find_device_by_address(self.jk.address) + ) device = loop.run_until_complete(t) - + if device is None: if tries > 2: return False From 7d0e167b98be2f66dd1e49d5a87d209283fdfd8b Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Sat, 21 Jan 2023 13:12:17 +0100 Subject: [PATCH 022/209] added info-message --- etc/dbus-serialbattery/jkbms_ble.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 91019a3d..f201a726 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -150,6 +150,7 @@ def refresh_data(self): return True def reset_bluetooth(self): + logger.info("reset of bluetooth triggered") if self.jk.is_running(): self.jk.stop_scraping() os.system("kill -9 $(pidof bluetoothd)") From edd9da8c0f2ef0214322244378b2ef84bba54cea Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Tue, 7 Feb 2023 18:36:32 +0000 Subject: [PATCH 023/209] workaround for dying bluetooth working --- etc/dbus-serialbattery/jkbms_ble.py | 25 ++++++++++++++++++------- etc/dbus-serialbattery/jkbms_brn.py | 14 +++++++++----- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index f201a726..d33bd1bf 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -10,6 +10,7 @@ class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" + resetting = False def __init__(self, port, baud, address): super(Jkbms_Ble, self).__init__("zero", baud) @@ -36,6 +37,7 @@ def test_connection(self): device = loop.run_until_complete(t) if device is None: + logger.info("jkbmsble not found") if tries > 2: return False else: @@ -104,13 +106,18 @@ def refresh_data(self): return False if time.time() - st["last_update"] > 30: # if data not updated for more than 30s, sth is wrong, then fail - if self.jk.is_running(): - # if the thread is still alive but data too old there is sth - # wrong with the bt-connection; restart whole stack - self.reset_connection() + logger.info("jkbmsble: bluetooth died") + + # if the thread is still alive but data too old there is sth + # wrong with the bt-connection; restart whole stack + if not self.resetting: + self.reset_bluetooth() self.jk.start_scraping() time.sleep(2) + return False + else: + self.resetting = False for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] @@ -151,10 +158,14 @@ def refresh_data(self): def reset_bluetooth(self): logger.info("reset of bluetooth triggered") - if self.jk.is_running(): - self.jk.stop_scraping() + self.resetting = True + # if self.jk.is_running(): + # self.jk.stop_scraping() + logger.info("scraping ended, issuing sys-commands") os.system("kill -9 $(pidof bluetoothd)") - time.sleep(1) + # os.system("/etc/init.d/bluetooth stop") is not enugh, kill -9 via pid is needed + time.sleep(2) os.system("rfkill block bluetooth") os.system("rfkill unblock bluetooth") os.system("/etc/init.d/bluetooth start") + logger.info("bluetooth should have been restarted") diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 22db5f10..c596f2cb 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -11,7 +11,7 @@ # zero means parse all incoming data (every second) CELL_INFO_REFRESH_S = 0 -DEVICE_INFO_REFRESH_S = 60 * 60 * 5 # every 5 Hours +DEVICE_INFO_REFRESH_S = 60 * 60 * 10 # every 10 Hours CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" @@ -87,7 +87,7 @@ class JkBmsBle: frame_buffer = bytearray() bms_status = {} - waiting_for_responsei = "" + waiting_for_response = "" last_cell_info = 0 def __init__(self, addr): @@ -308,6 +308,7 @@ async def asy_connect_and_scrape(self): client = BleakClient(self.address) print("btloop") try: + print("reconnect") await client.connect() self.bms_status["model_nbr"] = ( await client.read_gatt_char(MODEL_NBR_UUID) @@ -321,14 +322,17 @@ async def asy_connect_and_scrape(self): last_dev_info = time.time() while client.is_connected and self.run and self.main_thread.is_alive(): if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: - last_dev_info = time.time() - await self.request_bt("device_info", client) + await client.disconnect() await asyncio.sleep(0.01) except Exception as e: info("error while connecting to bt: " + str(e)) self.run = False finally: - await client.disconnect() + if client.is_connected: + try: + await client.disconnect() + except Exception as e: + info("error while disconnecting") print("Exiting bt-loop") From e35cc51e12801e557061df17ccd990c1fa6aca56 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 9 Feb 2023 14:43:51 +0100 Subject: [PATCH 024/209] added battery details - added: capacity - added: MOS temperature - added: charging switch status - added: discharging switch status - added: balancing switch status - added: show if balancing is active and which cells are balanced - added: cell imbalance alert --- etc/dbus-serialbattery/jkbms_ble.py | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index d6fb4fa3..1e86142a 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -73,6 +73,8 @@ def get_settings(self): for c in range(self.cell_count): self.cells.append(Cell(False)) + self.capacity = self.jk.get_status()["cell_info"]["capacity_nominal"] + self.hardware_version = ( "JKBMS " + self.jk.get_status()["device_info"]["hw_rev"] @@ -102,16 +104,37 @@ def refresh_data(self): self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) + self.to_temp('mos', st["cell_info"]["temperature_mos"]) self.current = st["cell_info"]["current"] self.voltage = st["cell_info"]["total_voltage"] self.soc = st["cell_info"]["battery_soc"] self.cycles = st["cell_info"]["cycle_count"] - self.capacity = st["cell_info"]["capacity_nominal"] + + self.charge_fet = st["settings"]["charging_switch"] + self.discharge_fet = st["settings"]["discharging_switch"] + self.balancing_fet = st["settings"]["balancing_switch"] + + self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True + self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 + self.balancing_action = st["cell_info"]["balancing_action"] + + for c in range(self.cell_count): + if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ): + self.cells[c].balance = True + else: + self.cells[c].balance = False # protection bits # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 - # self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 + + # trigger cell imbalance warning when delta is to great + if st["cell_info"]["delta_cell_voltage"] > 0.100: + self.protection.cell_imbalance = 2 + elif st["cell_info"]["delta_cell_voltage"] > 0.050: + self.protection.cell_imbalance = 1 + else: + self.protection.cell_imbalance = 0 self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 @@ -133,3 +156,7 @@ def refresh_data(self): 2 if st["warnings"]["discharge_overtemp"] else 0 ) return True + + + def get_balancing(self): + return 1 if self.balancing else 0 From a02e71dd677b7c036b6cbe89ff44b8866059ff69 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 11 Feb 2023 12:53:44 +0100 Subject: [PATCH 025/209] changed cell imbalance thresholds --- etc/dbus-serialbattery/jkbms_ble.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 1e86142a..1a41d844 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -113,7 +113,7 @@ def refresh_data(self): self.charge_fet = st["settings"]["charging_switch"] self.discharge_fet = st["settings"]["discharging_switch"] - self.balancing_fet = st["settings"]["balancing_switch"] + self.balance_fet = st["settings"]["balancing_switch"] self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 @@ -129,9 +129,9 @@ def refresh_data(self): # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 # trigger cell imbalance warning when delta is to great - if st["cell_info"]["delta_cell_voltage"] > 0.100: + if st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.05, 0.200): self.protection.cell_imbalance = 2 - elif st["cell_info"]["delta_cell_voltage"] > 0.050: + elif st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.03, 0.120): self.protection.cell_imbalance = 1 else: self.protection.cell_imbalance = 0 From 8bac054138a38e858814b8f531a1b3d62d2235b8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 12 Feb 2023 18:53:23 +0100 Subject: [PATCH 026/209] added battery details - added: MOS temperature - added: Balancing switch status --- etc/dbus-serialbattery/battery.py | 10 +++ etc/dbus-serialbattery/dbushelper.py | 8 ++- etc/dbus-serialbattery/installqml.sh | 5 ++ etc/dbus-serialbattery/jkbms.py | 5 ++ etc/dbus-serialbattery/qml/PageBattery.qml | 9 +++ etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 73 ++++++++++++++++++++ 6 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 etc/dbus-serialbattery/qml/PageLynxIonIo.qml diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index cdd700ec..28ac2412 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -53,10 +53,12 @@ def __init__(self, port, baud): self.soc = None self.charge_fet = None self.discharge_fet = None + self.balance_fet = None self.cell_count = None self.temp_sensors = None self.temp1 = None self.temp2 = None + self.temp_mos = None self.cells = [] self.control_charging = None self.control_voltage = None @@ -101,6 +103,8 @@ def to_temp(self, sensor, value): self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) + if sensor == 'mos': + self.temp_mos = min(max(value, -20), 100) def manage_charge_voltage(self): if LINEAR_LIMITATION_ENABLE: @@ -509,6 +513,12 @@ def get_max_temp(self): else: return None + def get_mos_temp(self): + if self.temp_mos is not None: + return self.temp_mos + else: + return None + def log_cell_data(self): if logger.getEffectiveLevel() > logging.INFO and len(self.cells) == 0: return False diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 83df19e4..35b0512e 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -224,6 +224,7 @@ def setup_vedbus(self): # Create battery extras self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) + self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", None, @@ -243,7 +244,8 @@ def setup_vedbus(self): self._dbusservice.add_path("/Balancing", None, writeable=True) self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) - # self._dbusservice.add_path('/SystemSwitch',1,writeable=True) + self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) + # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms self._dbusservice.add_path("/Alarms/LowVoltage", None, writeable=True) @@ -363,6 +365,9 @@ def publish_dbus(self): self._dbusservice["/Io/AllowToDischarge"] = ( 1 if self.battery.discharge_fet else 0 ) + self._dbusservice["/Io/AllowToBalance"] = ( + 1 if self.battery.balance_fet else 0 + ) self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None @@ -378,6 +383,7 @@ def publish_dbus(self): ) self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp() self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp() + self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control self._dbusservice[ diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index e4ac7237..3b880495 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -3,8 +3,13 @@ if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup fi +if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then + cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup +fi #copy new PageBattery.qml cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ +#copy new PageLynxIonIo.qml +cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/ #copy new PageBatteryCellVoltages cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/jkbms.py index f097cce1..07baaf8e 100644 --- a/etc/dbus-serialbattery/jkbms.py +++ b/etc/dbus-serialbattery/jkbms.py @@ -94,6 +94,11 @@ def read_status_data(self): self.to_temp(1, temp1 if temp1 < 99 else (100 - temp1)) self.to_temp(2, temp2 if temp2 < 99 else (100 - temp2)) + # MOSFET temperature + offset = cellbyte_count + 3 + temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] + self.to_temp('mos', temp_mos if temp_mos < 99 else (100 - temp_mos)) + offset = cellbyte_count + 12 voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0] self.voltage = voltage / 100 diff --git a/etc/dbus-serialbattery/qml/PageBattery.qml b/etc/dbus-serialbattery/qml/PageBattery.qml index ad8f6614..037b046f 100644 --- a/etc/dbus-serialbattery/qml/PageBattery.qml +++ b/etc/dbus-serialbattery/qml/PageBattery.qml @@ -119,6 +119,15 @@ MbPage { } } + MbItemValue { + description: qsTr("MOSFET temperature") + show: item.valid + item { + bind: service.path("/System/MOSTemperature") + displayUnit: user.temperatureUnit + } + } + MbItemValue { description: qsTr("Air temperature") item { diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml new file mode 100644 index 00000000..3a0acfff --- /dev/null +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -0,0 +1,73 @@ +import QtQuick 1.1 +import "utils.js" as Utils + +MbPage { + id: root + property string bindPrefix + + model: VisualItemModel { + MbItemOptions { + id: systemSwitch + description: qsTr("System Switch") + bind: Utils.path(bindPrefix, "/SystemSwitch") + readonly: true + possibleValues:[ + MbOption{description: qsTr("Disabled"); value: 0}, + MbOption{description: qsTr("Enabled"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to charge") + bind: Utils.path(bindPrefix, "/Io/AllowToCharge") + readonly: true + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to discharge") + bind: Utils.path(bindPrefix, "/Io/AllowToDischarge") + readonly: true + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to balance") + bind: service.path("/Io/AllowToBalance") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("External relay") + bind: Utils.path(bindPrefix, "/Io/ExternalRelay") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("Inactive"); value: 0}, + MbOption{description: qsTr("Active"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Programmable Contact") + bind: Utils.path(bindPrefix, "/Io/ProgrammableContact") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("Inactive"); value: 0}, + MbOption{description: qsTr("Active"); value: 1} + ] + } + } +} From a668a2442b7c64a6d3cf7271a82ba52bdc9c82ff Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 13 Feb 2023 06:49:40 +0100 Subject: [PATCH 027/209] JKBMS Bluetooth - added battery details (#454) * added battery details - added: capacity - added: MOS temperature - added: charging switch status - added: discharging switch status - added: balancing switch status - added: show if balancing is active and which cells are balanced - added: cell imbalance alert * changed cell imbalance thresholds * added battery details - added: MOS temperature - added: Balancing switch status --- etc/dbus-serialbattery/battery.py | 10 +++ etc/dbus-serialbattery/dbushelper.py | 8 ++- etc/dbus-serialbattery/installqml.sh | 5 ++ etc/dbus-serialbattery/jkbms.py | 5 ++ etc/dbus-serialbattery/jkbms_ble.py | 31 ++++++++- etc/dbus-serialbattery/qml/PageBattery.qml | 9 +++ etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 73 ++++++++++++++++++++ 7 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 etc/dbus-serialbattery/qml/PageLynxIonIo.qml diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index cdd700ec..28ac2412 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -53,10 +53,12 @@ def __init__(self, port, baud): self.soc = None self.charge_fet = None self.discharge_fet = None + self.balance_fet = None self.cell_count = None self.temp_sensors = None self.temp1 = None self.temp2 = None + self.temp_mos = None self.cells = [] self.control_charging = None self.control_voltage = None @@ -101,6 +103,8 @@ def to_temp(self, sensor, value): self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) + if sensor == 'mos': + self.temp_mos = min(max(value, -20), 100) def manage_charge_voltage(self): if LINEAR_LIMITATION_ENABLE: @@ -509,6 +513,12 @@ def get_max_temp(self): else: return None + def get_mos_temp(self): + if self.temp_mos is not None: + return self.temp_mos + else: + return None + def log_cell_data(self): if logger.getEffectiveLevel() > logging.INFO and len(self.cells) == 0: return False diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 83df19e4..35b0512e 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -224,6 +224,7 @@ def setup_vedbus(self): # Create battery extras self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) + self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", None, @@ -243,7 +244,8 @@ def setup_vedbus(self): self._dbusservice.add_path("/Balancing", None, writeable=True) self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) - # self._dbusservice.add_path('/SystemSwitch',1,writeable=True) + self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) + # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms self._dbusservice.add_path("/Alarms/LowVoltage", None, writeable=True) @@ -363,6 +365,9 @@ def publish_dbus(self): self._dbusservice["/Io/AllowToDischarge"] = ( 1 if self.battery.discharge_fet else 0 ) + self._dbusservice["/Io/AllowToBalance"] = ( + 1 if self.battery.balance_fet else 0 + ) self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None @@ -378,6 +383,7 @@ def publish_dbus(self): ) self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp() self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp() + self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control self._dbusservice[ diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index e4ac7237..3b880495 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -3,8 +3,13 @@ if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup fi +if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then + cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup +fi #copy new PageBattery.qml cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ +#copy new PageLynxIonIo.qml +cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/ #copy new PageBatteryCellVoltages cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/jkbms.py index f097cce1..07baaf8e 100644 --- a/etc/dbus-serialbattery/jkbms.py +++ b/etc/dbus-serialbattery/jkbms.py @@ -94,6 +94,11 @@ def read_status_data(self): self.to_temp(1, temp1 if temp1 < 99 else (100 - temp1)) self.to_temp(2, temp2 if temp2 < 99 else (100 - temp2)) + # MOSFET temperature + offset = cellbyte_count + 3 + temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] + self.to_temp('mos', temp_mos if temp_mos < 99 else (100 - temp_mos)) + offset = cellbyte_count + 12 voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0] self.voltage = voltage / 100 diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index d6fb4fa3..1a41d844 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -73,6 +73,8 @@ def get_settings(self): for c in range(self.cell_count): self.cells.append(Cell(False)) + self.capacity = self.jk.get_status()["cell_info"]["capacity_nominal"] + self.hardware_version = ( "JKBMS " + self.jk.get_status()["device_info"]["hw_rev"] @@ -102,16 +104,37 @@ def refresh_data(self): self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) + self.to_temp('mos', st["cell_info"]["temperature_mos"]) self.current = st["cell_info"]["current"] self.voltage = st["cell_info"]["total_voltage"] self.soc = st["cell_info"]["battery_soc"] self.cycles = st["cell_info"]["cycle_count"] - self.capacity = st["cell_info"]["capacity_nominal"] + + self.charge_fet = st["settings"]["charging_switch"] + self.discharge_fet = st["settings"]["discharging_switch"] + self.balance_fet = st["settings"]["balancing_switch"] + + self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True + self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 + self.balancing_action = st["cell_info"]["balancing_action"] + + for c in range(self.cell_count): + if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ): + self.cells[c].balance = True + else: + self.cells[c].balance = False # protection bits # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 - # self.protection.cell_imbalance = 1 if status["warnings"]["cell_imbalance"] else 0 + + # trigger cell imbalance warning when delta is to great + if st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.05, 0.200): + self.protection.cell_imbalance = 2 + elif st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.03, 0.120): + self.protection.cell_imbalance = 1 + else: + self.protection.cell_imbalance = 0 self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 @@ -133,3 +156,7 @@ def refresh_data(self): 2 if st["warnings"]["discharge_overtemp"] else 0 ) return True + + + def get_balancing(self): + return 1 if self.balancing else 0 diff --git a/etc/dbus-serialbattery/qml/PageBattery.qml b/etc/dbus-serialbattery/qml/PageBattery.qml index ad8f6614..037b046f 100644 --- a/etc/dbus-serialbattery/qml/PageBattery.qml +++ b/etc/dbus-serialbattery/qml/PageBattery.qml @@ -119,6 +119,15 @@ MbPage { } } + MbItemValue { + description: qsTr("MOSFET temperature") + show: item.valid + item { + bind: service.path("/System/MOSTemperature") + displayUnit: user.temperatureUnit + } + } + MbItemValue { description: qsTr("Air temperature") item { diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml new file mode 100644 index 00000000..3a0acfff --- /dev/null +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -0,0 +1,73 @@ +import QtQuick 1.1 +import "utils.js" as Utils + +MbPage { + id: root + property string bindPrefix + + model: VisualItemModel { + MbItemOptions { + id: systemSwitch + description: qsTr("System Switch") + bind: Utils.path(bindPrefix, "/SystemSwitch") + readonly: true + possibleValues:[ + MbOption{description: qsTr("Disabled"); value: 0}, + MbOption{description: qsTr("Enabled"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to charge") + bind: Utils.path(bindPrefix, "/Io/AllowToCharge") + readonly: true + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to discharge") + bind: Utils.path(bindPrefix, "/Io/AllowToDischarge") + readonly: true + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Allow to balance") + bind: service.path("/Io/AllowToBalance") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("No"); value: 0}, + MbOption{description: qsTr("Yes"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("External relay") + bind: Utils.path(bindPrefix, "/Io/ExternalRelay") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("Inactive"); value: 0}, + MbOption{description: qsTr("Active"); value: 1} + ] + } + + MbItemOptions { + description: qsTr("Programmable Contact") + bind: Utils.path(bindPrefix, "/Io/ProgrammableContact") + readonly: true + show: item.valid + possibleValues:[ + MbOption{description: qsTr("Inactive"); value: 0}, + MbOption{description: qsTr("Active"); value: 1} + ] + } + } +} From f07eb01361ecb414f89839dc28f5378f63544946 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Wed, 15 Feb 2023 15:34:56 +0000 Subject: [PATCH 028/209] added support for the newer 32s systems, that have slightly different frame-structure --- etc/dbus-serialbattery/jkbms_brn.py | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index c596f2cb..a6128980 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -56,12 +56,12 @@ TRANSLATE_CELL_INFO = [ - [["cell_info", "voltages", 16], 6, "= 112: + offset = 32 + elif translation[1] >= 54: + offset = 16 + i = 0 for j in kees: if isinstance(translation[2], int): # handle raw bytes without unpack_from; # 3. param gives no format but number of bytes val = bytearray( - fb[translation[1] + i : translation[1] + i + translation[2]] + fb[translation[1] + i + offset: translation[1] + i + translation[2] + offset] ) i += translation[2] else: val = unpack_from( - translation[2], bytearray(fb), translation[1] + i + translation[2], bytearray(fb), translation[1] + i + offset )[0] # calculate stepping in case of array i = i + calcsize(translation[2]) @@ -137,7 +145,7 @@ def translate(self, fb, translation, o, i=0): else: o[translation[0][i]] = {} - self.translate(fb, translation, o[translation[0][i]], i + 1) + self.translate(fb, translation, o[translation[0][i]], f32s=f32s, i=i + 1) def decode_warnings(self, fb): val = unpack_from(" 0) for t in TRANSLATE_CELL_INFO: - self.translate(fb, t, self.bms_status) + self.translate(fb, t, self.bms_status, f32s=has32s) self.decode_warnings(fb) debug(self.bms_status) @@ -184,6 +193,11 @@ def decode(self): info("Processing frame with settings info") if protocol_version == PROTOCOL_VERSION_JK02: self.decode_settings_jk02() + # adapt translation table for cell array lengths + ccount = self.bms_status["settings"]["cell_count"] + for i, t in enumerate(TRANSLATE_CELL_INFO): + if t[0][-2] == "voltages" or t[0][-2] == "voltages": + TRANSLATE_CELL_INFO[i][0][-1] = ccount self.bms_status["last_update"] = time.time() elif info_type == 0x02: @@ -347,11 +361,15 @@ def start_scraping(self): + " scraping thread: " + str(self.bt_thread.ident) ) - + def stop_scraping(self): self.run = False + stop = time.time() while self.is_running(): time.sleep(0.1) + if time.time() - stop > 10: + return False + return True def is_running(self): return self.bt_thread.is_alive() From 4e8bbb5cb3c1b9380bae30cdc997d4fdd4865542 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Thu, 16 Feb 2023 19:32:01 +0100 Subject: [PATCH 029/209] added stop of scraping if detection of jkbms fails --- etc/dbus-serialbattery/jkbms_ble.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index d9718808..e6cb2c33 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -62,9 +62,11 @@ def test_connection(self): # load initial data, from here on get_status has valid values to be served to the dbus status = self.jk.get_status() if status is None: + self.jk.stop_scraping() return False if not status["device_info"]["vendor_id"].startswith("JK-"): + self.jk.stop_scraping() return False logger.info("JK BMS found!") From c734938a7232e35d631eb8e6854bc5d28504d897 Mon Sep 17 00:00:00 2001 From: Eike Baran Date: Thu, 16 Feb 2023 19:43:50 +0100 Subject: [PATCH 030/209] lint --- etc/dbus-serialbattery/jkbms_ble.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index e6cb2c33..cbd1a450 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -118,33 +118,40 @@ def refresh_data(self): self.reset_bluetooth() self.jk.start_scraping() time.sleep(2) - + return False else: - self.resetting = False + self.resetting = False for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.to_temp('mos', st["cell_info"]["temperature_mos"]) + self.to_temp("mos", st["cell_info"]["temperature_mos"]) self.current = st["cell_info"]["current"] self.voltage = st["cell_info"]["total_voltage"] self.soc = st["cell_info"]["battery_soc"] self.cycles = st["cell_info"]["cycle_count"] - + self.charge_fet = st["settings"]["charging_switch"] self.discharge_fet = st["settings"]["discharging_switch"] self.balance_fet = st["settings"]["balancing_switch"] self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True - self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 + self.balancing_current = ( + st["cell_info"]["balancing_current"] + if st["cell_info"]["balancing_current"] < 32768 + else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 + ) self.balancing_action = st["cell_info"]["balancing_action"] for c in range(self.cell_count): - if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ): + if self.balancing and ( + st["cell_info"]["max_voltage_cell"] == c + or st["cell_info"]["min_voltage_cell"] == c + ): self.cells[c].balance = True else: self.cells[c].balance = False @@ -153,9 +160,13 @@ def refresh_data(self): # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 # trigger cell imbalance warning when delta is to great - if st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.05, 0.200): + if st["cell_info"]["delta_cell_voltage"] > min( + st["settings"]["cell_ovp"] * 0.05, 0.200 + ): self.protection.cell_imbalance = 2 - elif st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.03, 0.120): + elif st["cell_info"]["delta_cell_voltage"] > min( + st["settings"]["cell_ovp"] * 0.03, 0.120 + ): self.protection.cell_imbalance = 1 else: self.protection.cell_imbalance = 0 @@ -185,7 +196,7 @@ def reset_bluetooth(self): logger.info("reset of bluetooth triggered") self.resetting = True # if self.jk.is_running(): - # self.jk.stop_scraping() + # self.jk.stop_scraping() logger.info("scraping ended, issuing sys-commands") os.system("kill -9 $(pidof bluetoothd)") # os.system("/etc/init.d/bluetooth stop") is not enugh, kill -9 via pid is needed From ea6a1c333496def3e475f6ae7a7460a8f353d823 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 17 Feb 2023 10:52:45 +0100 Subject: [PATCH 031/209] indentation was wrong --- etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index 3a0acfff..cb522ac4 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -37,16 +37,16 @@ MbPage { ] } - MbItemOptions { - description: qsTr("Allow to balance") - bind: service.path("/Io/AllowToBalance") - readonly: true - show: item.valid + MbItemOptions { + description: qsTr("Allow to balance") + bind: service.path("/Io/AllowToBalance") + readonly: true + show: item.valid possibleValues:[ MbOption{description: qsTr("No"); value: 0}, MbOption{description: qsTr("Yes"); value: 1} ] - } + } MbItemOptions { description: qsTr("External relay") From fbbd17b549bfcec0b289a77df68d365ccf64f71f Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 20 Feb 2023 10:35:29 +0100 Subject: [PATCH 032/209] adding fix for Venus OS >= v3.00~14 QML files --- etc/dbus-serialbattery/installqml.sh | 68 ++++++++++++++++--- etc/dbus-serialbattery/qml/PageBattery.qml | 5 +- .../qml/PageBatteryCellVoltages.qml | 2 +- .../qml/PageBatterySetup.qml | 2 +- etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 2 +- 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index 3b880495..4641e6a3 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -1,21 +1,73 @@ -#!/bin/sh -#backup old PageBattery.qml once. New firmware upgrade will remove the backup +#!/bin/bash +set -x + +# elaborate version string for better comparing +# https://github.com/kwindrem/SetupHelper/blob/ebaa65fcf23e2bea6797f99c1c41174143c1153c/updateFileSets#L56-L81 +function versionStringToNumber () +{ + local local p4="" ; local p5="" ; local p5="" + local major=""; local minor="" + + # first character should be 'v' so first awk parameter will be empty and is not prited into the read command + # + # version number formats: v2.40, v2.40~6, v2.40-large-7, v2.40~6-large-7 + # so we must adjust how we use paramters read from the version string + # and parsed by awk + # if no beta make sure release is greater than any beta (i.e., a beta portion of 999) + + read major minor p4 p5 p6 <<< $(echo $1 | awk -v FS='[v.~-]' '{print $2, $3, $4, $5, $6}') + ((versionNumber = major * 1000000000 + minor * 1000000)) + if [ -z $p4 ] || [ $p4 = "large" ]; then + ((versionNumber += 999)) + else + ((versionNumber += p4)) + fi + if [ ! -z $p4 ] && [ $p4 = "large" ]; then + ((versionNumber += p5 * 1000)) + large=$p5 + elif [ ! -z $p6 ]; then + ((versionNumber += p6 * 1000)) + fi +} + +# get current Venus OS version +versionStringToNumber $(head -n 1 /opt/victronenergy/version) +((venusVersionNumber = $versionNumber)) + +# revert to VisualItemModel, if before v3.00~14 (v3.00~14 uses VisibleItemModel) +versionStringToNumber "v3.00~14" + +qmlDir="/data/etc/dbus-serialbattery/qml" + +if (( $venusVersionNumber < $versionNumber )); then + echo "Venus OS $(head -n 1 /opt/victronenergy/version) is olter than v3.00~14. Replacing VisibleItemModel with VisualItemModel" + fileList="$qmlDir/PageBattery.qml" + fileList+=" $qmlDir/PageBatteryCellVoltages.qml" + fileList+=" $qmlDir/PageBatterySetup.qml" + fileList+=" $qmlDir/PageLynxIonIo.qml" + for file in $fileList ; do + sed -i -e 's/VisibleItemModel/VisualItemModel/' "$file" + done +fi + + +# backup old PageBattery.qml once. New firmware upgrade will remove the backup if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup fi if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup fi -#copy new PageBattery.qml +# copy new PageBattery.qml cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ -#copy new PageLynxIonIo.qml +# copy new PageLynxIonIo.qml cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/ -#copy new PageBatteryCellVoltages +# copy new PageBatteryCellVoltages cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ -#stop gui +# stop gui svc -d /service/gui -#sleep 1 sec +# sleep 1 sec sleep 1 -#start gui +# start gui svc -u /service/gui diff --git a/etc/dbus-serialbattery/qml/PageBattery.qml b/etc/dbus-serialbattery/qml/PageBattery.qml index 037b046f..3f3b58f8 100644 --- a/etc/dbus-serialbattery/qml/PageBattery.qml +++ b/etc/dbus-serialbattery/qml/PageBattery.qml @@ -39,7 +39,7 @@ MbPage { } } - model: VisualItemModel { + model: VisibleItemModel { MbItemOptions { description: qsTr("Switch") bind: service.path("/Mode") @@ -310,6 +310,7 @@ MbPage { MbSubMenu { property VBusItem allowToCharge: VBusItem { bind: service.path("/Io/AllowToCharge") } + property VBusItem allowToBalance: VBusItem { bind: service.path("/Io/AllowToBalance") } description: qsTr("IO") subpage: Component { @@ -318,7 +319,7 @@ MbPage { bindPrefix: service.path("") } } - show: allowToCharge.valid + show: allowToCharge.valid || allowToBalance.valid } MbSubMenu { diff --git a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml index cbe1b49d..ccd65b28 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml @@ -78,7 +78,7 @@ MbPage { property string c24: _b24.valid && _b24.text == "1" ? "#ff0000" : "#ddd" title: service.description + " | Cell Voltages" - model: VisualItemModel { + model: VisibleItemModel { MbItemRow { description: qsTr("Cells Sum") diff --git a/etc/dbus-serialbattery/qml/PageBatterySetup.qml b/etc/dbus-serialbattery/qml/PageBatterySetup.qml index 5f732dbb..4d8e8fe1 100644 --- a/etc/dbus-serialbattery/qml/PageBatterySetup.qml +++ b/etc/dbus-serialbattery/qml/PageBatterySetup.qml @@ -36,7 +36,7 @@ MbPage { title: service.description + " | Cell Voltages" - model: VisualItemModel { + model: VisibleItemModel { MbSpinBox { description: qsTr("Maximum charge current") diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index cb522ac4..98ed18ce 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -5,7 +5,7 @@ MbPage { id: root property string bindPrefix - model: VisualItemModel { + model: VisibleItemModel { MbItemOptions { id: systemSwitch description: qsTr("System Switch") From 23a60db512f09d59e580a383e2bf5a58af7b3a46 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 5 Mar 2023 09:16:14 +0100 Subject: [PATCH 033/209] merge with baranator:dbus-serialbattery:master --- etc/dbus-serialbattery/jkbms_ble.py | 67 +++++++++++++++++++++++------ etc/dbus-serialbattery/jkbms_brn.py | 40 +++++++++++------ 2 files changed, 81 insertions(+), 26 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 1a41d844..33c35cb6 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -5,10 +5,12 @@ from bleak import BleakScanner, BleakError import asyncio import time +import os class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" + resetting = False def __init__(self, port, baud, address): super(Jkbms_Ble, self).__init__("zero", baud) @@ -25,20 +27,29 @@ def test_connection(self): # check if device with given mac is found, otherwise abort logger.info("test of jkbmsble") - try: - loop = asyncio.get_event_loop() - t = loop.create_task(BleakScanner.discover()) - devices = loop.run_until_complete(t) - except BleakError as e: - logger.error(str(e)) - return False - - found = False - for d in devices: - if d.address == self.jk.address: - found = True - if not found: - return False + tries = 0 + while True: + try: + loop = asyncio.get_event_loop() + t = loop.create_task( + BleakScanner.find_device_by_address(self.jk.address) + ) + device = loop.run_until_complete(t) + + if device is None: + logger.info("JkbmsBle not found") + if tries > 2: + return False + else: + # device found, exit loop and continue test + break + except BleakError as e: + if tries > 2: + return False + # recover from error if tries left + logger.error(str(e)) + self.reset_bluetooth() + tries += 1 # device was found, presumeably a jkbms so start scraping self.jk.start_scraping() @@ -51,9 +62,11 @@ def test_connection(self): # load initial data, from here on get_status has valid values to be served to the dbus status = self.jk.get_status() if status is None: + self.jk.stop_scraping() return False if not status["device_info"]["vendor_id"].startswith("JK-"): + self.jk.stop_scraping() return False logger.info("JK BMS found!") @@ -97,7 +110,18 @@ def refresh_data(self): return False if time.time() - st["last_update"] > 30: # if data not updated for more than 30s, sth is wrong, then fail + logger.info("jkbmsble: bluetooth died") + + # if the thread is still alive but data too old there is sth + # wrong with the bt-connection; restart whole stack + if not self.resetting: + self.reset_bluetooth() + self.jk.start_scraping() + time.sleep(2) + return False + else: + self.resetting = False for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] @@ -160,3 +184,18 @@ def refresh_data(self): def get_balancing(self): return 1 if self.balancing else 0 + + + def reset_bluetooth(self): + logger.info("reset of bluetooth triggered") + self.resetting = True + # if self.jk.is_running(): + # self.jk.stop_scraping() + logger.info("scraping ended, issuing sys-commands") + os.system("kill -9 $(pidof bluetoothd)") + # os.system("/etc/init.d/bluetooth stop") is not enugh, kill -9 via pid is needed + time.sleep(2) + os.system("rfkill block bluetooth") + os.system("rfkill unblock bluetooth") + os.system("/etc/init.d/bluetooth start") + logger.info("bluetooth should have been restarted") diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 5a3bea37..a6d6cbcb 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -11,7 +11,6 @@ # zero means parse all incoming data (every second) CELL_INFO_REFRESH_S = 0 -DEVICE_INFO_REFRESH_S = 60 * 60 * 5 # every 5 Hours CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" @@ -56,12 +55,12 @@ TRANSLATE_CELL_INFO = [ - [["cell_info", "voltages", 16], 6, "= 112: + offset = 32 + elif translation[1] >= 54: + offset = 16 i = 0 for j in kees: if isinstance(translation[2], int): # handle raw bytes without unpack_from; # 3. param gives no format but number of bytes val = bytearray( - fb[translation[1] + i : translation[1] + i + translation[2]] + fb[translation[1] + i + offset: translation[1] + i + translation[2] + offset] ) i += translation[2] else: val = unpack_from( - translation[2], bytearray(fb), translation[1] + i + translation[2], bytearray(fb), translation[1] + i + offset )[0] # calculate stepping in case of array i = i + calcsize(translation[2]) @@ -137,7 +142,7 @@ def translate(self, fb, translation, o, i=0): else: o[translation[0][i]] = {} - self.translate(fb, translation, o[translation[0][i]], i + 1) + self.translate(fb, translation, o[translation[0][i]], f32s=f32s, i=i + 1) def decode_warnings(self, fb): val = unpack_from(" 0) for t in TRANSLATE_DEVICE_INFO: - self.translate(fb, t, self.bms_status) + self.translate(fb, t, self.bms_status, f32s=has32s) def decode_cellinfo_jk02(self): fb = self.frame_buffer @@ -184,6 +190,11 @@ def decode(self): info("Processing frame with settings info") if protocol_version == PROTOCOL_VERSION_JK02: self.decode_settings_jk02() + # adapt translation table for cell array lengths + ccount = self.bms_status["settings"]["cell_count"] + for i, t in enumerate(TRANSLATE_CELL_INFO): + if t[0][-2] == "voltages" or t[0][-2] == "voltages": + TRANSLATE_CELL_INFO[i][0][-1] = ccount self.bms_status["last_update"] = time.time() elif info_type == 0x02: @@ -320,15 +331,16 @@ async def asy_connect_and_scrape(self): # await self.enable_charging(client) last_dev_info = time.time() while client.is_connected and self.run and self.main_thread.is_alive(): - if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: - last_dev_info = time.time() - await self.request_bt("device_info", client) await asyncio.sleep(0.01) except Exception as e: info("error while connecting to bt: " + str(e)) self.run = False finally: - await client.disconnect() + if client.is_connected: + try: + await client.disconnect() + except Exception as e: + info("error while disconnecting") print("Exiting bt-loop") @@ -346,8 +358,12 @@ def start_scraping(self): def stop_scraping(self): self.run = False + stop = time.time() while self.is_running(): time.sleep(0.1) + if time.time() - stop > 10: + return False + return True def is_running(self): return self.bt_thread.is_alive() From 2eae59119310c4b0b6998c809f0e147597c22c3a Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 5 Mar 2023 09:37:48 +0100 Subject: [PATCH 034/209] BLE JK BMS model recognition Pull request from @tcrichton https://github.com/Louisvdw/dbus-serialbattery/pull/462 --- etc/dbus-serialbattery/jkbms_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 33c35cb6..1d0b7baa 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -65,7 +65,7 @@ def test_connection(self): self.jk.stop_scraping() return False - if not status["device_info"]["vendor_id"].startswith("JK-"): + if not status["device_info"]["vendor_id"].startswith(("JK-", "JK_")): self.jk.stop_scraping() return False From d9518f2713ebfdfa212da15bd77e87a76cb6dc23 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 6 Mar 2023 16:33:37 +0100 Subject: [PATCH 035/209] Small fixes - Added: Setup cronjob when installing driver - Added: Information how to pair JKBMS changed default pin - Changed: Bump version to v0.15.0-ble --- etc/dbus-serialbattery/installlocal.sh | 12 +++++++++++- etc/dbus-serialbattery/jkbms_ble.py | 3 ++- etc/dbus-serialbattery/jkbms_brn.py | 5 ++--- etc/dbus-serialbattery/utils.py | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 76b1d6b5..72a37fa2 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,8 +1,18 @@ #!/bin/sh + +# install required packages opkg update opkg install python3-misc python3-pip pip3 install bleak + +# install driver tar -zxf ./venus-data.tar.gz -C /data sh /data/etc/dbus-serialbattery/reinstalllocal.sh -echo "make sure to disable Settings/Bluetooth in the Remote-Console to prevent reconnects every minute. In case of crash after ~12-16 hours disable raspberry pi 3 internal bluetooth via dtoverlay and use an external usb bluetooth-dongle" +# setup cronjob to restart Bluetooth +grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root + + +echo "Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo "ATTENTION: If you changed the default connection PIN of JKBMS, then you have to pair the JKBMS first using OS tools like the \"bluetoothctl\"." +echo "See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 1d0b7baa..29affc1c 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -143,8 +143,9 @@ def refresh_data(self): self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 self.balancing_action = st["cell_info"]["balancing_action"] + # show wich cells are balancing for c in range(self.cell_count): - if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ): + if self.balancing and ( st["cell_info"]["min_voltage_cell"] == c or st["cell_info"]["max_voltage_cell"] == c ): self.cells[c].balance = True else: self.cells[c].balance = False diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index a6d6cbcb..a2a6d45c 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -378,12 +378,11 @@ async def enable_charging(self, c): await self.write_register(0x1F, b"\x01\x00\x00\x00", 4, c) await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) - +''' if __name__ == "__main__": jk = JkBmsBle("C8:47:8C:E4:54:0E") - info("sss") jk.start_scraping() while True: - print("asdf") print(jk.get_status()) time.sleep(5) +''' diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index bb6eaea9..d6d03d95 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -28,8 +28,8 @@ ] # Constants - Need to dynamically get them in future -DRIVER_VERSION = 0.14 -DRIVER_SUBVERSION = "b3ble" +DRIVER_VERSION = '0.15' +DRIVER_SUBVERSION = ".0-ble" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From de39112fc52b037f298716f7d533df58e2be3bf6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 6 Mar 2023 16:38:09 +0100 Subject: [PATCH 036/209] Added driver restart script --- etc/dbus-serialbattery/restart-driver-and-ble.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 etc/dbus-serialbattery/restart-driver-and-ble.sh diff --git a/etc/dbus-serialbattery/restart-driver-and-ble.sh b/etc/dbus-serialbattery/restart-driver-and-ble.sh new file mode 100644 index 00000000..441dedf7 --- /dev/null +++ b/etc/dbus-serialbattery/restart-driver-and-ble.sh @@ -0,0 +1,6 @@ +#!/bin/sh +sh /data/etc/dbus-serialbattery/reinstalllocal.sh + +sh /data/etc/dbus-serialbattery/restartservice.sh + +/etc/init.d/bluetooth restart From a6668f6680b07b0efd5d30e6eb8695d2a2a85bcf Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 9 Mar 2023 16:14:07 +0100 Subject: [PATCH 037/209] added informations to run the driver --- etc/dbus-serialbattery/installlocal.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 72a37fa2..240f494b 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -14,5 +14,7 @@ grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || e echo "Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." -echo "ATTENTION: If you changed the default connection PIN of JKBMS, then you have to pair the JKBMS first using OS tools like the \"bluetoothctl\"." +echo "ATTENTION!" +echo "- At the moment this driver needs a serial to USB adapter attached to start. The serial side hasn't to be connected anywhere." +echo "- If you changed the default connection PIN of JKBMS, then you have to pair the JKBMS first using OS tools like the \"bluetoothctl\"." echo "See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." From c8a57966f38f1f0e2821b77f274be5e33f2c6801 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 9 Mar 2023 18:04:18 +0100 Subject: [PATCH 038/209] pasted code in wrong function --- etc/dbus-serialbattery/jkbms_brn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index a2a6d45c..31129457 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -166,14 +166,14 @@ def decode_warnings(self, fb): def decode_device_info_jk02(self): fb = self.frame_buffer - has32s = (fb[189] == 0x00 and fb[189 + 32] > 0) for t in TRANSLATE_DEVICE_INFO: - self.translate(fb, t, self.bms_status, f32s=has32s) + self.translate(fb, t, self.bms_status) def decode_cellinfo_jk02(self): fb = self.frame_buffer + has32s = (fb[189] == 0x00 and fb[189 + 32] > 0) for t in TRANSLATE_CELL_INFO: - self.translate(fb, t, self.bms_status) + self.translate(fb, t, self.bms_status, f32s=has32s) self.decode_warnings(fb) debug(self.bms_status) From dd146072c477203afc1e5f088061189433f04d91 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 10 Mar 2023 16:50:55 +0100 Subject: [PATCH 039/209] fixed cell balancing color --- .../qml/PageBatteryCellVoltages.qml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml index ccd65b28..112564df 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml @@ -142,10 +142,10 @@ MbPage { height: 22 show: volt17.valid values: [ - MbTextBlock { item: volt17; width: 70; height: 20; color: c13 }, - MbTextBlock { item: volt18; width: 70; height: 20; color: c14 }, - MbTextBlock { item: volt19; width: 70; height: 20; color: c15 }, - MbTextBlock { item: volt20; width: 70; height: 20; color: c16 } + MbTextBlock { item: volt17; width: 70; height: 20; color: c17 }, + MbTextBlock { item: volt18; width: 70; height: 20; color: c18 }, + MbTextBlock { item: volt19; width: 70; height: 20; color: c19 }, + MbTextBlock { item: volt20; width: 70; height: 20; color: c20 } ] } MbItemRow { @@ -153,10 +153,10 @@ MbPage { height: 22 show: volt21.valid values: [ - MbTextBlock { item: volt21; width: 70; height: 20; color: c13 }, - MbTextBlock { item: volt22; width: 70; height: 20; color: c14 }, - MbTextBlock { item: volt23; width: 70; height: 20; color: c15 }, - MbTextBlock { item: volt24; width: 70; height: 20; color: c16 } + MbTextBlock { item: volt21; width: 70; height: 20; color: c21 }, + MbTextBlock { item: volt22; width: 70; height: 20; color: c22 }, + MbTextBlock { item: volt23; width: 70; height: 20; color: c23 }, + MbTextBlock { item: volt24; width: 70; height: 20; color: c24 } ] } } From b05738d795951d0aa18c8450ea435cc8f1811deb Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Wed, 22 Mar 2023 14:38:33 +0100 Subject: [PATCH 040/209] BLE support for JBD JBD: Up to 4 Temp sensor JBD: Replace integer SoC value with floating-point calculation JBD: Add BMS SoC temperature JBD: Add temperature name indication for min/max temperature as T1, T2, T3 & T4 --- .flake8 | 1 + etc/dbus-serialbattery/battery.py | 54 ++++-- etc/dbus-serialbattery/dbus-serialbattery.py | 5 +- etc/dbus-serialbattery/dbushelper.py | 70 ++++---- etc/dbus-serialbattery/default_config.ini | 14 +- etc/dbus-serialbattery/lltjbd.py | 5 +- etc/dbus-serialbattery/lltjbd_ble.py | 155 ++++++++++++++++++ etc/dbus-serialbattery/mnb.py | 4 +- .../qml/PageBatteryCellVoltages.qml | 16 +- .../qml/PageBatteryDetails.qml | 68 ++++++++ requirements.txt | 1 + 11 files changed, 326 insertions(+), 67 deletions(-) create mode 100644 etc/dbus-serialbattery/lltjbd_ble.py create mode 100644 etc/dbus-serialbattery/qml/PageBatteryDetails.qml diff --git a/.flake8 b/.flake8 index 1b884f99..3ca7d5b5 100644 --- a/.flake8 +++ b/.flake8 @@ -9,6 +9,7 @@ exclude = ./etc/dbus-serialbattery/ecs.py, ./etc/dbus-serialbattery/lifepower.py, ./etc/dbus-serialbattery/lltjbd.py, + ./etc/dbus-serialbattery/lltjbd_ble.py, ./etc/dbus-serialbattery/minimalmodbus.py, ./etc/dbus-serialbattery/mnb.py, ./etc/dbus-serialbattery/renogy.py, diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 3b0df300..b4514e0f 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import statistics from typing import Union, Tuple, List from utils import logger @@ -77,6 +78,8 @@ def __init__(self, port, baud, address): self.temp_sensors = None self.temp1 = None self.temp2 = None + self.temp3 = None + self.temp4 = None self.temp_mos = None self.cells: List[Cell] = [] self.control_charging = None @@ -142,7 +145,11 @@ def to_temp(self, sensor: int, value: float) -> None: self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) - if sensor == 'mos': + if sensor == 3: + self.temp3 = min(max(value, -20), 100) + if sensor == 4: + self.temp4 = min(max(value, -20), 100) + if sensor == 0 or sensor == 'mos': self.temp_mos = min(max(value, -20), 100) def manage_charge_voltage(self) -> None: @@ -551,14 +558,10 @@ def get_balancing(self) -> int: return 1 return 0 - def extract_from_temp_values(self, extractor) -> Union[float, None]: - if self.temp1 is not None and self.temp2 is not None: - return extractor(self.temp1, self.temp2) - if self.temp1 is not None and self.temp2 is None: - return self.temp1 - if self.temp1 is None and self.temp2 is not None: - return self.temp2 - else: + def get_temperatures(self) -> Union[List[float], None]: + temperatures = [self.temp1, self.temp2, self.temp3, self.temp4] + result = [(t, i) for (t, i) in enumerate(temperatures) if t is not None] + if not result: return None def get_mos_temp(self): @@ -568,19 +571,34 @@ def get_mos_temp(self): return None def get_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: round((float(temp1) + float(temp2)) / 2, 2) - ) + temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + if not temps: + return None + return statistics.median(temps) + + def get_min_temp_cell_desc(self) -> Union[str, None]: + temps = [(t, i) for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) if t is not None] + if not temps: + return None + return "T" + str(min(temps)[1] + 1) def get_min_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: min(temp1, temp2) - ) + temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + if not temps: + return None + return min(temps) + + def get_max_temp_cell_desc(self) -> Union[str, None]: + temps = [(t, i) for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) if t is not None] + if not temps: + return None + return "T" + str(max(temps)[1] + 1) def get_max_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: max(temp1, temp2) - ) + temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + if not temps: + return None + return max(temps) def log_cell_data(self) -> bool: if logger.getEffectiveLevel() > logging.INFO and len(self.cells) == 0: diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 67fcfb26..db6a54b0 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -20,6 +20,7 @@ import utils from battery import Battery from lltjbd import LltJbd +from lltjbd_ble import LltJbdBle from daly import Daly from ant import Ant from jkbms import Jkbms @@ -31,10 +32,12 @@ supported_bms_types = [ {"bms": LltJbd, "baud": 9600}, + {"bms": LltJbdBle, "address": "70:3E:97:D2:F4:D0"}, {"bms": Ant, "baud": 19200}, {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Jkbms, "baud": 115200}, + {"bms": Jkbms_Ble, "address": "C8:47:8C:E4:54:0E"}, # {"bms" : Sinowealth}, {"bms": Lifepower, "baud": 9600}, {"bms": Renogy, "baud": 9600, "address": b"\x30"}, @@ -68,7 +71,7 @@ def get_battery(_port) -> Union[Battery, None]: for test in expected_bms_types: logger.info("Testing " + test["bms"].__name__) batteryClass = test["bms"] - baud = test["baud"] + baud = test.get("baud") battery: Battery = batteryClass( port=_port, baud=baud, address=test.get("address") ) diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index e1012b8c..7658c120 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -46,38 +46,39 @@ def setup_instance(self): path = "/Settings/Devices/serialbattery" default_instance = "battery:1" settings = { + # Name : [PATH, VALUE, MINIMUM, MAXIMUM, SILENT] "instance": [ path + "_" + str(bms_id).replace(" ", "_") + "/ClassAndVrmInstance", default_instance, 0, 0, ], - # 'CellVoltageMin': [path + '/CellVoltageMin', 2.8, 0.0, 5.0], - # 'CellVoltageMax': [path + '/CellVoltageMax', 3.45, 0.0, 5.0], - # 'CellVoltageFloat': [path + '/CellVoltageFloat', 3.35, 0.0, 5.0], - # 'VoltageMaxTime': [path + '/VoltageMaxTime', 900, 0, 0], - # 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', 90, 0, 100], - # 'MaxChargeCurrent': [path + '/MaxCurrentCharge', 5, 0.0, 500], - # 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', 7, 0.0, 500], - # 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], - # 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], - # 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], - # 'SocLowWarning': [path + '/SocLowWarning', 20, 0, 100], - # 'SocLowAlarm': [path + '/SocLowAlarm', 10, 0, 100], - # 'Capacity': [path + '/Capacity', '', 0, 500], - # 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', 0, 0, 1], - # 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', 98, 0, 100], - # 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', 95, 0, 100], - # 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', 91, 0, 100], - # 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', 10, 0, 100], - # 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', 20, 0, 100], - # 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', 30, 0, 100], - # 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', 5, 0, 100], - # 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', '', 0, 100], - # 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', '', 0, 100], - # 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', 5, 0, 100], - # 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', '', 0, 100], - # 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', '', 0, 100], + 'CellVoltageMin': [path + '/CellVoltageMin', MIN_CELL_VOLTAGE, 2.5, 3.65], + 'CellVoltageMax': [path + '/CellVoltageMax', MAX_CELL_VOLTAGE, 2.5, 3.65], + 'CellVoltageFloat': [path + '/CellVoltageFloat', FLOAT_CELL_VOLTAGE, 2.5, 3.65], + 'VoltageMaxTime': [path + '/VoltageMaxTime', MAX_VOLTAGE_TIME_SEC, 0, 0], + 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT, 0, 100], + 'MaxChargeCurrent': [path + '/MaxCurrentCharge', MAX_BATTERY_CHARGE_CURRENT, 0.0, 500], + 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', MAX_BATTERY_DISCHARGE_CURRENT, 0.0, 500], + 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], + 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], + 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], + 'SocLowWarning': [path + '/SocLowWarning', SOC_LOW_WARNING, 0, 100], + 'SocLowAlarm': [path + '/SocLowAlarm', SOC_LOW_ALARM, 0, 100], + 'Capacity': [path + '/Capacity', BATTERY_CAPACITY, 0, 500], + 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', INVERT_CURRENT_MEASUREMENT, 0, 1], + 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', CC_SOC_LIMIT1, 0, 100], + 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', CC_SOC_LIMIT2, 0, 100], + 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', CC_SOC_LIMIT3, 0, 100], + 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', CC_CURRENT_LIMIT1, 0, 500], + 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', CC_CURRENT_LIMIT2, 0, 500], + 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', CC_CURRENT_LIMIT3, 0, 500], + 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', DC_SOC_LIMIT1, 0, 100], + 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', DC_SOC_LIMIT2, 0, 100], + 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', DC_SOC_LIMIT3, 0, 100], + 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', DC_CURRENT_LIMIT1, 0, 500], + 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', DC_CURRENT_LIMIT2, 0, 500], + 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', DC_CURRENT_LIMIT3, 0, 500], } self.settings = SettingsDevice(get_bus(), settings, self.handle_changed_setting) @@ -183,9 +184,6 @@ def setup_vedbus(self): writeable=True, gettextcallback=lambda p, v: "{:0.0f}Ah".format(v), ) - # Not used at this stage - # self._dbusservice.add_path('/System/MinTemperatureCellId', None, writeable=True) - # self._dbusservice.add_path('/System/MaxTemperatureCellId', None, writeable=True) # Create SOC, DC and System items self._dbusservice.add_path("/Soc", None, writeable=True) @@ -222,9 +220,15 @@ def setup_vedbus(self): ) # Create battery extras + self._dbusservice.add_path("/System/MinTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) + self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) + self._dbusservice.add_path("/System/Temperature1", None, writeable=True) + self._dbusservice.add_path("/System/Temperature2", None, writeable=True) + self._dbusservice.add_path("/System/Temperature3", None, writeable=True) + self._dbusservice.add_path("/System/Temperature4", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", None, @@ -390,8 +394,14 @@ def publish_dbus(self): 0 if self.battery.online else 1 ) self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp() + self._dbusservice["/System/MinTemperatureCellId"] = self.battery.get_min_temp_cell_desc() self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp() + self._dbusservice["/System/MaxTemperatureCellId"] = self.battery.get_max_temp_cell_desc() self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() + self._dbusservice["/System/Temperature1"] = self.battery.temp1 + self._dbusservice["/System/Temperature2"] = self.battery.temp2 + self._dbusservice["/System/Temperature3"] = self.battery.temp3 + self._dbusservice["/System/Temperature4"] = self.battery.temp4 # Charge control self._dbusservice[ @@ -479,7 +489,7 @@ def publish_dbus(self): if ( self.battery.capacity is not None and len(TIME_TO_SOC_POINTS) > 0 - and self.battery.time_to_soc_update == 0 + and self.battery.time_to_soc_update <= 0 ): self.battery.time_to_soc_update = TIME_TO_SOC_LOOP_CYCLES crntPrctPerSec = ( diff --git a/etc/dbus-serialbattery/default_config.ini b/etc/dbus-serialbattery/default_config.ini index b9d96465..61a8c3f0 100644 --- a/etc/dbus-serialbattery/default_config.ini +++ b/etc/dbus-serialbattery/default_config.ini @@ -2,8 +2,10 @@ LINEAR_LIMITATION_ENABLE = False ; battery Current limits -MAX_BATTERY_CHARGE_CURRENT = 70.0 -MAX_BATTERY_DISCHARGE_CURRENT = 90.0 +# 70A * 48V / (18 * 3.45V) +MAX_BATTERY_CHARGE_CURRENT = 54.1 +# 5000 VA / (18 * 2.9V) +MAX_BATTERY_DISCHARGE_CURRENT = 80.46 ; -------- Cell Voltage limitation --------- ; Description: @@ -89,7 +91,7 @@ DC_CURRENT_LIMIT3_FRACTION = 0.5 CVCM_ENABLE = False ; Simulate Midpoint graph (True/False). -MIDPOINT_ENABLE = False +MIDPOINT_ENABLE = True ; soc low levels SOC_LOW_WARNING = 20 @@ -97,7 +99,7 @@ SOC_LOW_ALARM = 10 ; Daly settings ; Battery capacity (amps) if the BMS does not support reading it -BATTERY_CAPACITY = 50 +BATTERY_CAPACITY = 280 ; Invert Battery Current. Default non-inverted. Set to -1 to invert INVERT_CURRENT_MEASUREMENT = 1 @@ -107,7 +109,7 @@ INVERT_CURRENT_MEASUREMENT = 1 ; Every 5% SoC ; TIME_TO_SOC_POINTS = [100, 95, 90, 85, 75, 50, 25, 20, 10, 0] ; No data set to disable -TIME_TO_SOC_POINTS = +TIME_TO_SOC_POINTS=100,95,90,85,75,50,25,20,10,0 ; Specify TimeToSoc value type: [Valid values 1,2,3] ; TIME_TO_SOC_VALUE_TYPE = 1 ; Seconds ; TIME_TO_SOC_VALUE_TYPE = 2 ; Time string HH:MN:SC @@ -135,4 +137,4 @@ LIPRO_CELL_COUNT = 15 PUBLISH_CONFIG_VALUES = 1 -BMS_TYPE = \ No newline at end of file +BMS_TYPE = LltJbdBle \ No newline at end of file diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index 9af90c02..765bc73f 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -138,13 +138,14 @@ def read_gen_data(self): balance2, protection, version, - self.soc, + soc, fet, self.cell_count, self.temp_sensors, ) = unpack_from(">HhHHHHhHHBBBBB", gen_data) self.voltage = voltage / 100 self.current = current / 100 + self.soc = capacity_remain / capacity * 100 self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) @@ -156,7 +157,7 @@ def read_gen_data(self): for t in range(self.temp_sensors): temp1 = unpack_from(">H", gen_data, 23 + (2 * t))[0] - self.to_temp(t + 1, kelvin_to_celsius(temp1 / 10)) + self.to_temp(t, kelvin_to_celsius(temp1 / 10)) return True diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/lltjbd_ble.py new file mode 100644 index 00000000..8296954a --- /dev/null +++ b/etc/dbus-serialbattery/lltjbd_ble.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +import asyncio +import atexit +import threading +from typing import Union, Optional + +from bleak import BleakClient + +from utils import * +from struct import * +from lltjbd import LltJbdProtection, LltJbd + +BLE_SERVICE_UUID = "0000ff00-0000-1000-8000-00805f9b34fb" +BLE_CHARACTERISTICS_TX_UUID = "0000ff02-0000-1000-8000-00805f9b34fb" +BLE_CHARACTERISTICS_RX_UUID = "0000ff01-0000-1000-8000-00805f9b34fb" +MIN_RESPONSE_SIZE = 6 +MAX_RESPONSE_SIZE = 256 + +class LltJbdBle(LltJbd): + + def __init__(self, port: str, baud: Optional[int], address: Optional[str]): + super(LltJbdBle, self).__init__(port, -1, address) + + self.address = address + self.protection = LltJbdProtection() + self.type = self.BATTERYTYPE + self.main_thread = threading.current_thread() + self.bt_loop = asyncio.new_event_loop() + self.data: bytearray = bytearray() + self.run = True + self.bt_thread = threading.Thread(name="BLELoop", target=self.background_loop, args=(self.bt_loop, ), daemon=True) + self.bt_client: Optional[BleakClient] = None + + def on_disconnect(self, client): + logger.info("BLE client disconnected") + + async def bt_main_loop(self): + async with BleakClient(self.address, disconnected_callback=self.on_disconnect) as client: + await client.start_notify(BLE_CHARACTERISTICS_RX_UUID, self.ncallback) + await asyncio.sleep(1) + self.bt_client = client + self.name + while self.run and client.is_connected and self.main_thread.is_alive(): + await asyncio.sleep(1) + await client.stop_notify(BLE_CHARACTERISTICS_RX_UUID) + + def background_loop(self, loop: asyncio.AbstractEventLoop): + asyncio.set_event_loop(loop) + while self.run and self.main_thread.is_alive(): + loop.run_until_complete(self.bt_main_loop()) + sleep(0.01) + loop.stop() + + def test_connection(self): + if not self.address: + return False + + if not self.bt_thread.is_alive(): + self.bt_thread.start() + + def shutdown_ble_atexit(thread): + self.run = False + thread.join() + + atexit.register(shutdown_ble_atexit, self.bt_thread) + count = 0 + while not self.bt_client: + count += 1 + sleep(0.2) + if count == 10: + return False + return super().test_connection() + + def ncallback(self, sender, data: bytearray): + self.data.extend(data) + + async def send_command(self, command) -> Union[bytearray, bool]: + await self.bt_client.write_gatt_char(BLE_CHARACTERISTICS_TX_UUID, command, False) + await asyncio.sleep(0.5) + result = await self.read_bluetooth_data() + return result + + async def read_bluetooth_data( + self + ) -> Union[bytearray, bool]: + count = 0 + while len(self.data) < (self.LENGTH_POS + 1): + await asyncio.sleep(0.01) + count += 1 + if count > 50: + break + + if len(self.data) < (self.LENGTH_POS + 1): + if len(self.data) == 0: + logger.error(">>> ERROR: No reply - returning") + else: + logger.error( + ">>> ERROR: No reply - returning [len:" + str(len(self.data)) + "]" + ) + return False + + length = self.data[self.LENGTH_POS] + count = 0 + while len(self.data) <= length + self.LENGTH_POS: + await asyncio.sleep(0.005) + count += 1 + if count > 150: + logger.error( + ">>> ERROR: No reply - returning [len:" + + str(len(self.data)) + + "/" + + str(length + self.LENGTH_POS) + + "]" + ) + return False + + result = self.data + self.data = bytearray() + return result + + def read_serial_data_llt(self, command): + task = asyncio.run_coroutine_threadsafe(self.send_command(command), self.bt_loop) + try: + data = task.result(timeout=2) + except: + logger.error(">>> ERROR: No reply - returning") + return False + if not data: + return False + + start, flag, command_ret, length = unpack_from('BBBB', data) + checksum, end = unpack_from('HB', data, length + 4) + + if end == 119: + return data[4:length + 4] + else: + logger.error(">>> ERROR: Incorrect Reply") + return False + + +async def testBLE(): + import sys + bat = LltJbdBle("Foo", -1, sys.argv[1]) + if not bat.test_connection(): + logger.error(">>> ERROR: Unable to connect") + else: + bat.refresh_data() + bat.refresh_data() + bat.refresh_data() + print("Done") + + +if __name__ == "__main__": + asyncio.run(testBLE()) + diff --git a/etc/dbus-serialbattery/mnb.py b/etc/dbus-serialbattery/mnb.py index 8e91bd5b..e82089c4 100644 --- a/etc/dbus-serialbattery/mnb.py +++ b/etc/dbus-serialbattery/mnb.py @@ -161,9 +161,9 @@ def manage_charge_current(self): # Change depending on the cell_min_voltage values if self.cell_min_voltage < self.V_C_min + 0.05: - self.control_allow_dicharge = False + self.control_allow_discharge = False else: - self.control_allow_dicharge = True + self.control_allow_discharge = True if self.cell_min_voltage < self.V_C_min + 0.15: b = 10 * (self.cell_min_voltage - self.V_C_min - 0.05) diff --git a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml index cbe1b49d..47476093 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml @@ -142,10 +142,10 @@ MbPage { height: 22 show: volt17.valid values: [ - MbTextBlock { item: volt17; width: 70; height: 20; color: c13 }, - MbTextBlock { item: volt18; width: 70; height: 20; color: c14 }, - MbTextBlock { item: volt19; width: 70; height: 20; color: c15 }, - MbTextBlock { item: volt20; width: 70; height: 20; color: c16 } + MbTextBlock { item: volt17; width: 70; height: 20; color: c17 }, + MbTextBlock { item: volt18; width: 70; height: 20; color: c18 }, + MbTextBlock { item: volt19; width: 70; height: 20; color: c19 }, + MbTextBlock { item: volt20; width: 70; height: 20; color: c20 } ] } MbItemRow { @@ -153,10 +153,10 @@ MbPage { height: 22 show: volt21.valid values: [ - MbTextBlock { item: volt21; width: 70; height: 20; color: c13 }, - MbTextBlock { item: volt22; width: 70; height: 20; color: c14 }, - MbTextBlock { item: volt23; width: 70; height: 20; color: c15 }, - MbTextBlock { item: volt24; width: 70; height: 20; color: c16 } + MbTextBlock { item: volt21; width: 70; height: 20; color: c21 }, + MbTextBlock { item: volt22; width: 70; height: 20; color: c22 }, + MbTextBlock { item: volt23; width: 70; height: 20; color: c23 }, + MbTextBlock { item: volt24; width: 70; height: 20; color: c24 } ] } } diff --git a/etc/dbus-serialbattery/qml/PageBatteryDetails.qml b/etc/dbus-serialbattery/qml/PageBatteryDetails.qml new file mode 100644 index 00000000..a96f9609 --- /dev/null +++ b/etc/dbus-serialbattery/qml/PageBatteryDetails.qml @@ -0,0 +1,68 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 +import "utils.js" as Utils + +MbPage { + id: root + + property string bindPrefix + property BatteryDetails details: BatteryDetails { bindPrefix: root.bindPrefix } + + model: VisualItemModel { + MbItemRow { + description: qsTr("Lowest cell voltage") + values: [ + MbTextBlock { item.text: details.minVoltageCellId.text }, + MbTextBlock { item.text: details.minCellVoltage.text } + ] + } + + MbItemRow { + description: qsTr("Highest cell voltage") + values: [ + MbTextBlock { item.text: details.maxVoltageCellId.text }, + MbTextBlock { item.text: details.maxCellVoltage.text } + ] + } + + MbItemRow { + description: qsTr("Minimum cell temperature") + values: [ + MbTextBlock { item.text: details.minTemperatureCellId.text }, + MbTextBlock { item.text: details.minCellTemperature.text } + ] + } + + MbItemRow { + description: qsTr("Maximum cell temperature") + values: [ + MbTextBlock { item.text: details.maxTemperatureCellId.text }, + MbTextBlock { item.text: details.maxCellTemperature.text } + ] + } + + MbItemRow { + description: qsTr("Battery modules") + values: [ + MbTextBlock { item.text: details.modulesOnline.text }, + MbTextBlock { item.text: details.modulesOffline.text } + ] + } + + MbItemRow { + description: qsTr("Nr. of modules blocking charge / discharge") + values: [ + MbTextBlock { item.text: details.nrOfModulesBlockingCharge.text }, + MbTextBlock { item.text: details.nrOfModulesBlockingDischarge.text } + ] + } + + MbItemRow { + description: qsTr("Installed / Available capacity") + values: [ + MbTextBlock { item.text: details.installedCapacity.text }, + MbTextBlock { item.bind: Utils.path(bindPrefix, "/Capacity") } + ] + } + } +} diff --git a/requirements.txt b/requirements.txt index 8bec2184..1ecd7997 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pyserial==3.5 minimalmodbus==2.0.1 +bleak==0.19.5 \ No newline at end of file From dd2f7b352aa3198bc3448b629929d24fb2fdc562 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 21:05:47 +0100 Subject: [PATCH 041/209] feature: Improve BLE async communication bugfix: Deal with slow first initial request feature: Resilient BLE communication after disconnect feature: Bump Bleak for fixed disconnect handling & BLE address search on macOS --- etc/dbus-serialbattery/battery.py | 9 ++ etc/dbus-serialbattery/dbushelper.py | 10 +- etc/dbus-serialbattery/default_config.ini | 2 +- etc/dbus-serialbattery/lltjbd.py | 10 +- etc/dbus-serialbattery/lltjbd_ble.py | 152 +++++++++++----------- requirements.txt | 2 +- 6 files changed, 101 insertions(+), 84 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index b4514e0f..cda843b4 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -110,6 +110,15 @@ def test_connection(self) -> bool: # return false when failed, true if successful return False + def connection_name(self) -> str: + return "Serial " + self.port + + def custom_name(self) -> str: + return "SerialBattery(" + self.type + ")" + + def product_name(self) -> str: + return "SerialBattery(" + self.type + ")" + @abstractmethod def get_settings(self) -> bool: """ diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 7658c120..4dd3f646 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -116,13 +116,13 @@ def setup_vedbus(self): self._dbusservice.add_path( "/Mgmt/ProcessVersion", "Python " + platform.python_version() ) - self._dbusservice.add_path("/Mgmt/Connection", "Serial " + self.battery.port) + self._dbusservice.add_path("/Mgmt/Connection", self.battery.connection_name()) # Create the mandatory objects self._dbusservice.add_path("/DeviceInstance", self.instance) self._dbusservice.add_path("/ProductId", 0x0) self._dbusservice.add_path( - "/ProductName", "SerialBattery(" + self.battery.type + ")" + "/ProductName", self.battery.product_name() ) self._dbusservice.add_path( "/FirmwareVersion", str(DRIVER_VERSION) + DRIVER_SUBVERSION @@ -130,7 +130,7 @@ def setup_vedbus(self): self._dbusservice.add_path("/HardwareVersion", self.battery.hardware_version) self._dbusservice.add_path("/Connected", 1) self._dbusservice.add_path( - "/CustomName", "SerialBattery(" + self.battery.type + ")", writeable=True + "/CustomName", self.battery.custom_name(), writeable=True ) # Create static battery info @@ -489,7 +489,7 @@ def publish_dbus(self): if ( self.battery.capacity is not None and len(TIME_TO_SOC_POINTS) > 0 - and self.battery.time_to_soc_update <= 0 + and self.battery.time_to_soc_update == 0 ): self.battery.time_to_soc_update = TIME_TO_SOC_LOOP_CYCLES crntPrctPerSec = ( @@ -498,7 +498,7 @@ def publish_dbus(self): for num in TIME_TO_SOC_POINTS: self._dbusservice["/TimeToSoC/" + str(num)] = ( - self.battery.get_timetosoc(num, crntPrctPerSec) + self.battery.get_timetosoc(float(num), crntPrctPerSec) if self.battery.current else None ) diff --git a/etc/dbus-serialbattery/default_config.ini b/etc/dbus-serialbattery/default_config.ini index 61a8c3f0..609db2b7 100644 --- a/etc/dbus-serialbattery/default_config.ini +++ b/etc/dbus-serialbattery/default_config.ini @@ -114,7 +114,7 @@ TIME_TO_SOC_POINTS=100,95,90,85,75,50,25,20,10,0 ; TIME_TO_SOC_VALUE_TYPE = 1 ; Seconds ; TIME_TO_SOC_VALUE_TYPE = 2 ; Time string HH:MN:SC ; Both Seconds and time str " [days, HR:MN:SC]" -TIME_TO_SOC_VALUE_TYPE = 3 +TIME_TO_SOC_VALUE_TYPE = 1 ; Specify how many loop cycles between each TimeToSoc updates TIME_TO_SOC_LOOP_CYCLES = 5 ; Include TimeToSoC points when moving away from the SoC point. [Valid values True,False] diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index 765bc73f..87aeb4a2 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -49,6 +49,7 @@ def __init__(self, port, baud, address): super(LltJbd, self).__init__(port, baud, address) self.protection = LltJbdProtection() self.type = self.BATTERYTYPE + self._product_name: str = "" # degree_sign = u'\N{DEGREE SIGN}' command_general = b"\xDD\xA5\x03\x00\xFF\xFD\x77" @@ -67,6 +68,9 @@ def test_connection(self): return result + def product_name(self) -> str: + return self._product_name + def get_settings(self): self.read_gen_data() self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT @@ -149,7 +153,7 @@ def read_gen_data(self): self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) - self.version = float(str(version >> 4 & 0x0F) + "." + str(version & 0x0F)) + self.hardware_version = float(str(version >> 4 & 0x0F) + "." + str(version & 0x0F)) self.to_fet_bits(fet) self.to_protection_bits(protection) self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count @@ -182,10 +186,10 @@ def read_hardware_data(self): if hardware_data is False: return False - self.hardware_version = unpack_from( + self._product_name = unpack_from( ">" + str(len(hardware_data)) + "s", hardware_data )[0].decode() - logger.debug(self.hardware_version) + logger.debug(self._product_name) return True def read_serial_data_llt(self, command): diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/lltjbd_ble.py index 8296954a..9a8af779 100644 --- a/etc/dbus-serialbattery/lltjbd_ble.py +++ b/etc/dbus-serialbattery/lltjbd_ble.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- import asyncio import atexit +import functools import threading from typing import Union, Optional -from bleak import BleakClient +from bleak import BleakClient, BleakScanner, BLEDevice from utils import * from struct import * @@ -25,36 +26,48 @@ def __init__(self, port: str, baud: Optional[int], address: Optional[str]): self.protection = LltJbdProtection() self.type = self.BATTERYTYPE self.main_thread = threading.current_thread() - self.bt_loop = asyncio.new_event_loop() self.data: bytearray = bytearray() self.run = True - self.bt_thread = threading.Thread(name="BLELoop", target=self.background_loop, args=(self.bt_loop, ), daemon=True) + self.bt_thread = threading.Thread(name="BLELoop", target=self.background_loop, daemon=True) + self.bt_loop: Optional[asyncio.AbstractEventLoop] = None self.bt_client: Optional[BleakClient] = None + self.device: Optional[BLEDevice] = None + self.response_queue: Optional[asyncio.Queue] = None + self.ready_event: Optional[asyncio.Event] = None + + def connection_name(self) -> str: + return "BLE " + self.address + + def custom_name(self) -> str: + return self.device.name def on_disconnect(self, client): logger.info("BLE client disconnected") async def bt_main_loop(self): - async with BleakClient(self.address, disconnected_callback=self.on_disconnect) as client: - await client.start_notify(BLE_CHARACTERISTICS_RX_UUID, self.ncallback) - await asyncio.sleep(1) + self.device = await BleakScanner.find_device_by_address( + self.address, cb=dict(use_bdaddr=True) + ) + + if not self.device: + self.run = False + return + + async with BleakClient(self.device, disconnected_callback=self.on_disconnect) as client: self.bt_client = client - self.name + self.bt_loop = asyncio.get_event_loop() + self.response_queue = asyncio.Queue() + self.ready_event.set() while self.run and client.is_connected and self.main_thread.is_alive(): - await asyncio.sleep(1) - await client.stop_notify(BLE_CHARACTERISTICS_RX_UUID) + await asyncio.sleep(0.1) + self.bt_loop = None - def background_loop(self, loop: asyncio.AbstractEventLoop): - asyncio.set_event_loop(loop) + def background_loop(self): while self.run and self.main_thread.is_alive(): - loop.run_until_complete(self.bt_main_loop()) - sleep(0.01) - loop.stop() - - def test_connection(self): - if not self.address: - return False + asyncio.run(self.bt_main_loop()) + async def async_test_connection(self): + self.ready_event = asyncio.Event() if not self.bt_thread.is_alive(): self.bt_thread.start() @@ -63,68 +76,62 @@ def shutdown_ble_atexit(thread): thread.join() atexit.register(shutdown_ble_atexit, self.bt_thread) - count = 0 - while not self.bt_client: - count += 1 - sleep(0.2) - if count == 10: - return False - return super().test_connection() + try: + return await asyncio.wait_for(self.ready_event.wait(), timeout=5) + except asyncio.TimeoutError: + logger.error(">>> ERROR: Unable to connect with BLE device") + return False - def ncallback(self, sender, data: bytearray): - self.data.extend(data) + def test_connection(self): + if not self.address: + return False - async def send_command(self, command) -> Union[bytearray, bool]: - await self.bt_client.write_gatt_char(BLE_CHARACTERISTICS_TX_UUID, command, False) - await asyncio.sleep(0.5) - result = await self.read_bluetooth_data() - return result + if not asyncio.run(self.async_test_connection()): + return False + return super().test_connection() - async def read_bluetooth_data( - self - ) -> Union[bytearray, bool]: - count = 0 - while len(self.data) < (self.LENGTH_POS + 1): - await asyncio.sleep(0.01) - count += 1 - if count > 50: - break - - if len(self.data) < (self.LENGTH_POS + 1): - if len(self.data) == 0: - logger.error(">>> ERROR: No reply - returning") - else: - logger.error( - ">>> ERROR: No reply - returning [len:" + str(len(self.data)) + "]" - ) + async def send_command(self, command) -> Union[bytearray, bool]: + if not self.bt_client: + logger.error(">>> ERROR: No BLE client connection - returning") return False - length = self.data[self.LENGTH_POS] - count = 0 - while len(self.data) <= length + self.LENGTH_POS: - await asyncio.sleep(0.005) - count += 1 - if count > 150: - logger.error( - ">>> ERROR: No reply - returning [len:" - + str(len(self.data)) - + "/" - + str(length + self.LENGTH_POS) - + "]" - ) - return False - - result = self.data - self.data = bytearray() + fut = self.bt_loop.create_future() + + def rx_callback(future: asyncio.Future, data: bytearray, sender, rx: bytearray): + data.extend(rx) + if len(data) < (self.LENGTH_POS + 1): + return + + length = data[self.LENGTH_POS] + if len(data) <= length + self.LENGTH_POS + 1: + return + if not future.done(): + future.set_result(data) + + rx_collector = functools.partial(rx_callback, fut, bytearray()) + await self.bt_client.start_notify(BLE_CHARACTERISTICS_RX_UUID, rx_collector) + await self.bt_client.write_gatt_char(BLE_CHARACTERISTICS_TX_UUID, command, False) + result = await fut + await self.bt_client.stop_notify(BLE_CHARACTERISTICS_RX_UUID) + return result - def read_serial_data_llt(self, command): - task = asyncio.run_coroutine_threadsafe(self.send_command(command), self.bt_loop) + async def async_read_serial_data_llt(self, command): try: - data = task.result(timeout=2) - except: + bt_task = asyncio.run_coroutine_threadsafe(self.send_command(command), self.bt_loop) + result = await asyncio.wait_for(asyncio.wrap_future(bt_task), 20) + return result + except asyncio.TimeoutError: logger.error(">>> ERROR: No reply - returning") return False + except Exception as e: + logger.error(">>> ERROR: No reply - returning", e) + return False + + def read_serial_data_llt(self, command): + if not self.bt_loop: + return False + data = asyncio.run(self.async_read_serial_data_llt(command)) if not data: return False @@ -145,11 +152,8 @@ async def testBLE(): logger.error(">>> ERROR: Unable to connect") else: bat.refresh_data() - bat.refresh_data() - bat.refresh_data() - print("Done") if __name__ == "__main__": - asyncio.run(testBLE()) + testBLE() diff --git a/requirements.txt b/requirements.txt index 1ecd7997..6de24eea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ pyserial==3.5 minimalmodbus==2.0.1 -bleak==0.19.5 \ No newline at end of file +bleak==0.20.0 \ No newline at end of file From 99e1835a0f8145e91a9c1b8e34678c8325a752db Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 22:41:41 +0100 Subject: [PATCH 042/209] bugfix: Failing dbus-systemcalc due to ':' in /Mgmt/Connection --- etc/dbus-serialbattery/lltjbd_ble.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/lltjbd_ble.py index 9a8af779..a96a8b9c 100644 --- a/etc/dbus-serialbattery/lltjbd_ble.py +++ b/etc/dbus-serialbattery/lltjbd_ble.py @@ -36,7 +36,8 @@ def __init__(self, port: str, baud: Optional[int], address: Optional[str]): self.ready_event: Optional[asyncio.Event] = None def connection_name(self) -> str: - return "BLE " + self.address + # TODO investigate failing dbus-systemcalc due to ':' in self.address + return "BLE" def custom_name(self) -> str: return self.device.name From 3d140f18cd457b296f5b6a1acfe138916f19e2c8 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 22:53:35 +0100 Subject: [PATCH 043/209] clean-up --- .../qml/PageBatteryDetails.qml | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 etc/dbus-serialbattery/qml/PageBatteryDetails.qml diff --git a/etc/dbus-serialbattery/qml/PageBatteryDetails.qml b/etc/dbus-serialbattery/qml/PageBatteryDetails.qml deleted file mode 100644 index a96f9609..00000000 --- a/etc/dbus-serialbattery/qml/PageBatteryDetails.qml +++ /dev/null @@ -1,68 +0,0 @@ -import QtQuick 1.1 -import com.victron.velib 1.0 -import "utils.js" as Utils - -MbPage { - id: root - - property string bindPrefix - property BatteryDetails details: BatteryDetails { bindPrefix: root.bindPrefix } - - model: VisualItemModel { - MbItemRow { - description: qsTr("Lowest cell voltage") - values: [ - MbTextBlock { item.text: details.minVoltageCellId.text }, - MbTextBlock { item.text: details.minCellVoltage.text } - ] - } - - MbItemRow { - description: qsTr("Highest cell voltage") - values: [ - MbTextBlock { item.text: details.maxVoltageCellId.text }, - MbTextBlock { item.text: details.maxCellVoltage.text } - ] - } - - MbItemRow { - description: qsTr("Minimum cell temperature") - values: [ - MbTextBlock { item.text: details.minTemperatureCellId.text }, - MbTextBlock { item.text: details.minCellTemperature.text } - ] - } - - MbItemRow { - description: qsTr("Maximum cell temperature") - values: [ - MbTextBlock { item.text: details.maxTemperatureCellId.text }, - MbTextBlock { item.text: details.maxCellTemperature.text } - ] - } - - MbItemRow { - description: qsTr("Battery modules") - values: [ - MbTextBlock { item.text: details.modulesOnline.text }, - MbTextBlock { item.text: details.modulesOffline.text } - ] - } - - MbItemRow { - description: qsTr("Nr. of modules blocking charge / discharge") - values: [ - MbTextBlock { item.text: details.nrOfModulesBlockingCharge.text }, - MbTextBlock { item.text: details.nrOfModulesBlockingDischarge.text } - ] - } - - MbItemRow { - description: qsTr("Installed / Available capacity") - values: [ - MbTextBlock { item.text: details.installedCapacity.text }, - MbTextBlock { item.bind: Utils.path(bindPrefix, "/Capacity") } - ] - } - } -} From a37244f45b73d702287e8acb9bcfbe36ccad179f Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 22:54:00 +0100 Subject: [PATCH 044/209] Fix dbus-systemcalc requires for TimeToGo integer value only --- etc/dbus-serialbattery/battery.py | 10 +++++----- etc/dbus-serialbattery/dbushelper.py | 4 ++-- etc/dbus-serialbattery/lltjbd_ble.py | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index cda843b4..9ac1e96a 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -465,7 +465,7 @@ def get_capacity_remain(self) -> Union[float, None]: return self.capacity * self.soc / 100 return None - def get_timetosoc(self, socnum, crntPrctPerSec) -> str: + def get_timetosoc(self, socnum, crntPrctPerSec, value_type) -> str: if self.current > 0: diffSoc = socnum - self.soc else: @@ -476,13 +476,13 @@ def get_timetosoc(self, socnum, crntPrctPerSec) -> str: secondstogo = int(diffSoc / crntPrctPerSec) ttgStr = "" - if utils.TIME_TO_SOC_VALUE_TYPE & 1: + if value_type & 1: ttgStr += str(secondstogo) - if utils.TIME_TO_SOC_VALUE_TYPE & 2: + if value_type & 2: ttgStr += " [" - if utils.TIME_TO_SOC_VALUE_TYPE & 2: + if value_type & 2: ttgStr += str(timedelta(seconds=secondstogo)) - if utils.TIME_TO_SOC_VALUE_TYPE & 1: + if value_type & 1: ttgStr += "]" return ttgStr diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 4dd3f646..3048be90 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -498,14 +498,14 @@ def publish_dbus(self): for num in TIME_TO_SOC_POINTS: self._dbusservice["/TimeToSoC/" + str(num)] = ( - self.battery.get_timetosoc(float(num), crntPrctPerSec) + self.battery.get_timetosoc(float(num), crntPrctPerSec, TIME_TO_SOC_VALUE_TYPE) if self.battery.current else None ) # Update TimeToGo self._dbusservice["/TimeToGo"] = ( - self.battery.get_timetosoc(SOC_LOW_WARNING, crntPrctPerSec) + int(self.battery.get_timetosoc(SOC_LOW_WARNING, crntPrctPerSec, 1)) if self.battery.current else None ) diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/lltjbd_ble.py index a96a8b9c..9a8af779 100644 --- a/etc/dbus-serialbattery/lltjbd_ble.py +++ b/etc/dbus-serialbattery/lltjbd_ble.py @@ -36,8 +36,7 @@ def __init__(self, port: str, baud: Optional[int], address: Optional[str]): self.ready_event: Optional[asyncio.Event] = None def connection_name(self) -> str: - # TODO investigate failing dbus-systemcalc due to ':' in self.address - return "BLE" + return "BLE " + self.address def custom_name(self) -> str: return self.device.name From 30dacf680884ffd90d36290f4fa3f14eb3c46442 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 23:00:59 +0100 Subject: [PATCH 045/209] clean-up #2 --- etc/dbus-serialbattery/dbus-serialbattery.py | 2 - etc/dbus-serialbattery/dbushelper.py | 52 +-- etc/dbus-serialbattery/default_config.ini | 6 +- etc/dbus-serialbattery/jkbms_ble.py | 162 -------- etc/dbus-serialbattery/jkbms_brn.py | 373 ------------------- 5 files changed, 29 insertions(+), 566 deletions(-) delete mode 100644 etc/dbus-serialbattery/jkbms_ble.py delete mode 100644 etc/dbus-serialbattery/jkbms_brn.py diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index db6a54b0..a4d914e2 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -24,7 +24,6 @@ from daly import Daly from ant import Ant from jkbms import Jkbms -from jkbms_ble import Jkbms_Ble # from sinowealth import Sinowealth from renogy import Renogy from ecs import Ecs @@ -37,7 +36,6 @@ {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Jkbms, "baud": 115200}, - {"bms": Jkbms_Ble, "address": "C8:47:8C:E4:54:0E"}, # {"bms" : Sinowealth}, {"bms": Lifepower, "baud": 9600}, {"bms": Renogy, "baud": 9600, "address": b"\x30"}, diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 3048be90..c1dea4bb 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -53,32 +53,32 @@ def setup_instance(self): 0, 0, ], - 'CellVoltageMin': [path + '/CellVoltageMin', MIN_CELL_VOLTAGE, 2.5, 3.65], - 'CellVoltageMax': [path + '/CellVoltageMax', MAX_CELL_VOLTAGE, 2.5, 3.65], - 'CellVoltageFloat': [path + '/CellVoltageFloat', FLOAT_CELL_VOLTAGE, 2.5, 3.65], - 'VoltageMaxTime': [path + '/VoltageMaxTime', MAX_VOLTAGE_TIME_SEC, 0, 0], - 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT, 0, 100], - 'MaxChargeCurrent': [path + '/MaxCurrentCharge', MAX_BATTERY_CHARGE_CURRENT, 0.0, 500], - 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', MAX_BATTERY_DISCHARGE_CURRENT, 0.0, 500], - 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], - 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], - 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], - 'SocLowWarning': [path + '/SocLowWarning', SOC_LOW_WARNING, 0, 100], - 'SocLowAlarm': [path + '/SocLowAlarm', SOC_LOW_ALARM, 0, 100], - 'Capacity': [path + '/Capacity', BATTERY_CAPACITY, 0, 500], - 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', INVERT_CURRENT_MEASUREMENT, 0, 1], - 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', CC_SOC_LIMIT1, 0, 100], - 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', CC_SOC_LIMIT2, 0, 100], - 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', CC_SOC_LIMIT3, 0, 100], - 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', CC_CURRENT_LIMIT1, 0, 500], - 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', CC_CURRENT_LIMIT2, 0, 500], - 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', CC_CURRENT_LIMIT3, 0, 500], - 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', DC_SOC_LIMIT1, 0, 100], - 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', DC_SOC_LIMIT2, 0, 100], - 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', DC_SOC_LIMIT3, 0, 100], - 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', DC_CURRENT_LIMIT1, 0, 500], - 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', DC_CURRENT_LIMIT2, 0, 500], - 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', DC_CURRENT_LIMIT3, 0, 500], + # 'CellVoltageMin': [path + '/CellVoltageMin', MIN_CELL_VOLTAGE, 2.5, 3.65], + # 'CellVoltageMax': [path + '/CellVoltageMax', MAX_CELL_VOLTAGE, 2.5, 3.65], + # 'CellVoltageFloat': [path + '/CellVoltageFloat', FLOAT_CELL_VOLTAGE, 2.5, 3.65], + # 'VoltageMaxTime': [path + '/VoltageMaxTime', MAX_VOLTAGE_TIME_SEC, 0, 0], + # 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT, 0, 100], + # 'MaxChargeCurrent': [path + '/MaxCurrentCharge', MAX_BATTERY_CHARGE_CURRENT, 0.0, 500], + # 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', MAX_BATTERY_DISCHARGE_CURRENT, 0.0, 500], + # 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], + # 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], + # 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], + # 'SocLowWarning': [path + '/SocLowWarning', SOC_LOW_WARNING, 0, 100], + # 'SocLowAlarm': [path + '/SocLowAlarm', SOC_LOW_ALARM, 0, 100], + # 'Capacity': [path + '/Capacity', BATTERY_CAPACITY, 0, 500], + # 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', INVERT_CURRENT_MEASUREMENT, 0, 1], + # 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', CC_SOC_LIMIT1, 0, 100], + # 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', CC_SOC_LIMIT2, 0, 100], + # 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', CC_SOC_LIMIT3, 0, 100], + # 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', CC_CURRENT_LIMIT1, 0, 500], + # 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', CC_CURRENT_LIMIT2, 0, 500], + # 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', CC_CURRENT_LIMIT3, 0, 500], + # 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', DC_SOC_LIMIT1, 0, 100], + # 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', DC_SOC_LIMIT2, 0, 100], + # 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', DC_SOC_LIMIT3, 0, 100], + # 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', DC_CURRENT_LIMIT1, 0, 500], + # 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', DC_CURRENT_LIMIT2, 0, 500], + # 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', DC_CURRENT_LIMIT3, 0, 500], } self.settings = SettingsDevice(get_bus(), settings, self.handle_changed_setting) diff --git a/etc/dbus-serialbattery/default_config.ini b/etc/dbus-serialbattery/default_config.ini index 609db2b7..22775f65 100644 --- a/etc/dbus-serialbattery/default_config.ini +++ b/etc/dbus-serialbattery/default_config.ini @@ -91,7 +91,7 @@ DC_CURRENT_LIMIT3_FRACTION = 0.5 CVCM_ENABLE = False ; Simulate Midpoint graph (True/False). -MIDPOINT_ENABLE = True +MIDPOINT_ENABLE = False ; soc low levels SOC_LOW_WARNING = 20 @@ -99,7 +99,7 @@ SOC_LOW_ALARM = 10 ; Daly settings ; Battery capacity (amps) if the BMS does not support reading it -BATTERY_CAPACITY = 280 +BATTERY_CAPACITY = 50 ; Invert Battery Current. Default non-inverted. Set to -1 to invert INVERT_CURRENT_MEASUREMENT = 1 @@ -114,7 +114,7 @@ TIME_TO_SOC_POINTS=100,95,90,85,75,50,25,20,10,0 ; TIME_TO_SOC_VALUE_TYPE = 1 ; Seconds ; TIME_TO_SOC_VALUE_TYPE = 2 ; Time string HH:MN:SC ; Both Seconds and time str " [days, HR:MN:SC]" -TIME_TO_SOC_VALUE_TYPE = 1 +TIME_TO_SOC_VALUE_TYPE = 3 ; Specify how many loop cycles between each TimeToSoc updates TIME_TO_SOC_LOOP_CYCLES = 5 ; Include TimeToSoC points when moving away from the SoC point. [Valid values True,False] diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py deleted file mode 100644 index 1a41d844..00000000 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ /dev/null @@ -1,162 +0,0 @@ -# -*- coding: utf-8 -*- -from battery import Battery, Cell -from utils import logger -from jkbms_brn import JkBmsBle -from bleak import BleakScanner, BleakError -import asyncio -import time - - -class Jkbms_Ble(Battery): - BATTERYTYPE = "Jkbms BLE" - - def __init__(self, port, baud, address): - super(Jkbms_Ble, self).__init__("zero", baud) - self.type = self.BATTERYTYPE - self.jk = JkBmsBle(address) - - logger.error("init of jkbmsble") - - def test_connection(self): - # call a function that will connect to the battery, send a command and retrieve the result. - # The result or call should be unique to this BMS. Battery name or version, etc. - # Return True if success, False for failure - - # check if device with given mac is found, otherwise abort - - logger.info("test of jkbmsble") - try: - loop = asyncio.get_event_loop() - t = loop.create_task(BleakScanner.discover()) - devices = loop.run_until_complete(t) - except BleakError as e: - logger.error(str(e)) - return False - - found = False - for d in devices: - if d.address == self.jk.address: - found = True - if not found: - return False - - # device was found, presumeably a jkbms so start scraping - self.jk.start_scraping() - tries = 1 - - while self.jk.get_status() is None and tries < 20: - time.sleep(0.5) - tries += 1 - - # load initial data, from here on get_status has valid values to be served to the dbus - status = self.jk.get_status() - if status is None: - return False - - if not status["device_info"]["vendor_id"].startswith("JK-"): - return False - - logger.info("JK BMS found!") - return True - - def get_settings(self): - # After successful connection get_settings will be call to set up the battery. - # Set the current limits, populate cell count, etc - # Return True if success, False for failure - st = self.jk.get_status()["settings"] - self.cell_count = st["cell_count"] - self.max_battery_charge_current = st["max_charge_current"] - self.max_battery_discharge_current = st["max_discharge_current"] - self.max_battery_voltage = st["cell_ovp"] * self.cell_count - self.min_battery_voltage = st["cell_uvp"] * self.cell_count - - for c in range(self.cell_count): - self.cells.append(Cell(False)) - - self.capacity = self.jk.get_status()["cell_info"]["capacity_nominal"] - - self.hardware_version = ( - "JKBMS " - + self.jk.get_status()["device_info"]["hw_rev"] - + " " - + str(self.cell_count) - + " cells" - ) - logger.info("BAT: " + self.hardware_version) - return True - - def refresh_data(self): - # call all functions that will refresh the battery data. - # This will be called for every iteration (1 second) - # Return True if success, False for failure - - # result = self.read_soc_data() - # TODO: check for errors - st = self.jk.get_status() - if st is None: - return False - if time.time() - st["last_update"] > 30: - # if data not updated for more than 30s, sth is wrong, then fail - return False - - for c in range(self.cell_count): - self.cells[c].voltage = st["cell_info"]["voltages"][c] - - self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) - self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.to_temp('mos', st["cell_info"]["temperature_mos"]) - self.current = st["cell_info"]["current"] - self.voltage = st["cell_info"]["total_voltage"] - - self.soc = st["cell_info"]["battery_soc"] - self.cycles = st["cell_info"]["cycle_count"] - - self.charge_fet = st["settings"]["charging_switch"] - self.discharge_fet = st["settings"]["discharging_switch"] - self.balance_fet = st["settings"]["balancing_switch"] - - self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True - self.balancing_current = st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 - self.balancing_action = st["cell_info"]["balancing_action"] - - for c in range(self.cell_count): - if self.balancing and (st["cell_info"]["max_voltage_cell"] == c or st["cell_info"]["min_voltage_cell"] == c ): - self.cells[c].balance = True - else: - self.cells[c].balance = False - - # protection bits - # self.protection.soc_low = 2 if status["cell_info"]["battery_soc"] < 10.0 else 0 - - # trigger cell imbalance warning when delta is to great - if st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.05, 0.200): - self.protection.cell_imbalance = 2 - elif st["cell_info"]["delta_cell_voltage"] > min(st["settings"]["cell_ovp"] * 0.03, 0.120): - self.protection.cell_imbalance = 1 - else: - self.protection.cell_imbalance = 0 - - self.protection.voltage_high = 2 if st["warnings"]["cell_overvoltage"] else 0 - self.protection.voltage_low = 2 if st["warnings"]["cell_undervoltage"] else 0 - - self.protection.current_over = ( - 2 - if ( - st["warnings"]["charge_overcurrent"] - or st["warnings"]["discharge_overcurrent"] - ) - else 0 - ) - self.protection.set_IC_inspection = ( - 2 if st["cell_info"]["temperature_mos"] > 80 else 0 - ) - self.protection.temp_high_charge = 2 if st["warnings"]["charge_overtemp"] else 0 - self.protection.temp_low_charge = 2 if st["warnings"]["charge_undertemp"] else 0 - self.protection.temp_high_discharge = ( - 2 if st["warnings"]["discharge_overtemp"] else 0 - ) - return True - - - def get_balancing(self): - return 1 if self.balancing else 0 diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py deleted file mode 100644 index 5a3bea37..00000000 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ /dev/null @@ -1,373 +0,0 @@ -import asyncio -from bleak import BleakScanner, BleakClient -import time -from logging import info, debug -import logging -from struct import unpack_from, calcsize -import threading - -logging.basicConfig(level=logging.INFO) - - -# zero means parse all incoming data (every second) -CELL_INFO_REFRESH_S = 0 -DEVICE_INFO_REFRESH_S = 60 * 60 * 5 # every 5 Hours -CHAR_HANDLE = "0000ffe1-0000-1000-8000-00805f9b34fb" -MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb" - -COMMAND_CELL_INFO = 0x96 -COMMAND_DEVICE_INFO = 0x97 - -FRAME_VERSION_JK04 = 0x01 -FRAME_VERSION_JK02 = 0x02 -FRAME_VERSION_JK02_32S = 0x03 -PROTOCOL_VERSION_JK02 = 0x02 - - -protocol_version = PROTOCOL_VERSION_JK02 - - -MIN_RESPONSE_SIZE = 300 -MAX_RESPONSE_SIZE = 320 - -TRANSLATE_DEVICE_INFO = [ - [["device_info", "hw_rev"], 22, "8s"], - [["device_info", "sw_rev"], 30, "8s"], - [["device_info", "uptime"], 38, " CELL_INFO_REFRESH_S - ): - self.last_cell_info = time.time() - info("processing frame with battery cell info") - if protocol_version == PROTOCOL_VERSION_JK02: - self.decode_cellinfo_jk02() - self.bms_status["last_update"] = time.time() - # power is calculated from voltage x current as - # register 122 contains unsigned power-value - self.bms_status["cell_info"]["power"] = ( - self.bms_status["cell_info"]["current"] - * self.bms_status["cell_info"]["total_voltage"] - ) - if self.waiting_for_response == "cell_info": - self.waiting_for_response = "" - - elif info_type == 0x03: - info("processing frame with device info") - if protocol_version == PROTOCOL_VERSION_JK02: - self.decode_device_info_jk02() - self.bms_status["last_update"] = time.time() - else: - return - if self.waiting_for_response == "device_info": - self.waiting_for_response = "" - - def assemble_frame(self, data: bytearray): - if len(self.frame_buffer) > MAX_RESPONSE_SIZE: - info("data dropped because it alone was longer than max frame length") - self.frame_buffer = [] - - if data[0] == 0x55 and data[1] == 0xAA and data[2] == 0xEB and data[3] == 0x90: - # beginning of new frame, clear buffer - self.frame_buffer = [] - - self.frame_buffer.extend(data) - - if len(self.frame_buffer) >= MIN_RESPONSE_SIZE: - # check crc; always at position 300, independent of - # actual frame-lentgh, so crc up to 299 - ccrc = self.crc(self.frame_buffer, 300 - 1) - rcrc = self.frame_buffer[300 - 1] - debug(f"compair recvd. crc: {rcrc} vs calc. crc: {ccrc}") - if ccrc == rcrc: - debug("great success! frame complete and sane, lets decode") - self.decode() - self.frame_buffer = [] - - def ncallback(self, sender: int, data: bytearray): - debug(f"------> NEW PACKAGE!laenge: {len(data)}") - self.assemble_frame(data) - - def crc(self, arr: bytearray, length: int) -> int: - crc = 0 - for a in arr[:length]: - crc = crc + a - return crc.to_bytes(2, "little")[0] - - async def write_register( - self, address, vals: bytearray, length: int, bleakC: BleakClient - ): - frame = bytearray(20) - frame[0] = 0xAA # start sequence - frame[1] = 0x55 # start sequence - frame[2] = 0x90 # start sequence - frame[3] = 0xEB # start sequence - frame[4] = address # holding register - frame[5] = length # size of the value in byte - frame[6] = vals[0] - frame[7] = vals[1] - frame[8] = vals[2] - frame[9] = vals[3] - frame[10] = 0x00 - frame[11] = 0x00 - frame[12] = 0x00 - frame[13] = 0x00 - frame[14] = 0x00 - frame[15] = 0x00 - frame[16] = 0x00 - frame[17] = 0x00 - frame[18] = 0x00 - frame[19] = self.crc(frame, len(frame) - 1) - debug("Write register: ", frame) - await bleakC.write_gatt_char(CHAR_HANDLE, frame, False) - - async def request_bt(self, rtype: str, client): - timeout = time.time() - - while self.waiting_for_response != "" and time.time() - timeout < 10: - await asyncio.sleep(1) - print(self.waiting_for_response) - - if rtype == "cell_info": - cmd = COMMAND_CELL_INFO - self.waiting_for_response = "cell_info" - elif rtype == "device_info": - cmd = COMMAND_DEVICE_INFO - self.waiting_for_response = "device_info" - else: - return - - await self.write_register(cmd, b"\0\0\0\0", 0x00, client) - - def get_status(self): - if "settings" in self.bms_status and "cell_info" in self.bms_status: - return self.bms_status - else: - return None - - def connect_and_scrape(self): - asyncio.run(self.asy_connect_and_scrape()) - - async def asy_connect_and_scrape(self): - print("connect and scrape on address: " + self.address) - self.run = True - while self.run and self.main_thread.is_alive(): # autoreconnect - client = BleakClient(self.address) - print("btloop") - try: - await client.connect() - self.bms_status["model_nbr"] = ( - await client.read_gatt_char(MODEL_NBR_UUID) - ).decode("utf-8") - - await client.start_notify(CHAR_HANDLE, self.ncallback) - await self.request_bt("device_info", client) - - await self.request_bt("cell_info", client) - # await self.enable_charging(client) - last_dev_info = time.time() - while client.is_connected and self.run and self.main_thread.is_alive(): - if time.time() - last_dev_info > DEVICE_INFO_REFRESH_S: - last_dev_info = time.time() - await self.request_bt("device_info", client) - await asyncio.sleep(0.01) - except Exception as e: - info("error while connecting to bt: " + str(e)) - self.run = False - finally: - await client.disconnect() - - print("Exiting bt-loop") - - def start_scraping(self): - self.main_thread = threading.current_thread() - if self.is_running(): - return - self.bt_thread.start() - info( - "scraping thread started -> main thread id: " - + str(self.main_thread.ident) - + " scraping thread: " - + str(self.bt_thread.ident) - ) - - def stop_scraping(self): - self.run = False - while self.is_running(): - time.sleep(0.1) - - def is_running(self): - return self.bt_thread.is_alive() - - async def enable_charging(self, c): - # these are the registers for the control-buttons: - # data is 01 00 00 00 for on 00 00 00 00 for off; - # the following bytes up to 19 are unclear and changing - # dynamically -> auth-mechanism? - await self.write_register(0x1D, b"\x01\x00\x00\x00", 4, c) - await self.write_register(0x1E, b"\x01\x00\x00\x00", 4, c) - await self.write_register(0x1F, b"\x01\x00\x00\x00", 4, c) - await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) - - -if __name__ == "__main__": - jk = JkBmsBle("C8:47:8C:E4:54:0E") - info("sss") - jk.start_scraping() - while True: - print("asdf") - print(jk.get_status()) - time.sleep(5) From eecd51275f0cc486a7444a00d36850af3078d15a Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 23:02:45 +0100 Subject: [PATCH 046/209] clean-up --- etc/dbus-serialbattery/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 17a161d5..032cfedf 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = 0.14 -DRIVER_SUBVERSION = "b3ble" +DRIVER_SUBVERSION = ".3" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 7009e65a014a3802418b26d093616b81a8b6ed80 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 23 Mar 2023 23:06:31 +0100 Subject: [PATCH 047/209] Remove dependency on statistics (not available by default on VenusOS) --- etc/dbus-serialbattery/battery.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 9ac1e96a..1367f62d 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import statistics from typing import Union, Tuple, List from utils import logger @@ -581,9 +580,15 @@ def get_mos_temp(self): def get_temp(self) -> Union[float, None]: temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] - if not temps: + n = len(temps) + if not temps or n == 0: return None - return statistics.median(temps) + data = sorted(temps) + if n % 2 == 1: + return data[n // 2] + else: + i = n // 2 + return (data[i - 1] + data[i]) / 2 def get_min_temp_cell_desc(self) -> Union[str, None]: temps = [(t, i) for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) if t is not None] From 3f67884969ae4ddcb062011c800967a1d8599c54 Mon Sep 17 00:00:00 2001 From: Tristan Crichton Date: Mon, 27 Mar 2023 10:59:37 +0100 Subject: [PATCH 048/209] BLE JK BMS model recognition (#462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * * Other JK BMS’ have an underscore in their vendor_id * Don’t forget to include your new PageLynxIonIo in the build lis * build list is removed upstream --------- Co-authored-by: Tristan Crichton --- etc/dbus-serialbattery/jkbms_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 1a41d844..228d0bd6 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -53,7 +53,7 @@ def test_connection(self): if status is None: return False - if not status["device_info"]["vendor_id"].startswith("JK-"): + if not status["device_info"]["vendor_id"].startswith(("JK-", "JK_")): return False logger.info("JK BMS found!") From b0a97e3999e9fec665f2371beadf4b08a39702cf Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Tue, 28 Mar 2023 16:03:22 +0000 Subject: [PATCH 049/209] install BLE battery as local independent service --- etc/dbus-serialbattery/dbus-serialbattery.py | 12 +++++++++++- etc/dbus-serialbattery/installble.sh | 15 +++++++++++++++ etc/dbus-serialbattery/installlocal.sh | 3 ++- etc/dbus-serialbattery/jkbms_ble.py | 4 ++-- etc/dbus-serialbattery/reinstalllocal.sh | 2 +- etc/dbus-serialbattery/utils.py | 1 - 6 files changed, 31 insertions(+), 6 deletions(-) create mode 100755 etc/dbus-serialbattery/installble.sh diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index d74bbd8b..1f07858e 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -81,7 +81,17 @@ def get_port(): logger.info("dbus-serialbattery v" + str(DRIVER_VERSION) + DRIVER_SUBVERSION) port = get_port() - battery = get_battery_type(port) + battery = None + if port.endswith("_Ble") and len(sys.argv) > 2: + class_ = eval(port) + testbms = class_("", 9600, sys.argv[2]) + if testbms.test_connection() is True: + logger.info( + "Connection established to " + testbms.__class__.__name__ + ) + battery = testbms + else: + battery = get_battery_type(port) # exit if no battery could be found if battery is None: diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh new file mode 100755 index 00000000..19dd2ad2 --- /dev/null +++ b/etc/dbus-serialbattery/installble.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +## DO NOT TOUCH THIS ## +install_service() { + mkdir -p /service/dbus-blebattery-$1 + echo '#!/bin/sh' > /service/dbus-blebattery-$1/run + echo 'python /data/etc/dbus-serialbattery/dbus-serialbattery.py ' "$2" "$3" >> /service/dbus-blebattery-$1/run + chmod 755 /service/dbus-blebattery-$1/run +} +## END DO NOT TOUCH AREA ## + +## Uncomment for each adapter here, increase the number for each service + +# install_service 0 Jkbms_Ble C8:47:8C:12:34:56 +# install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 76b1d6b5..91abce5f 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -5,4 +5,5 @@ pip3 install bleak tar -zxf ./venus-data.tar.gz -C /data sh /data/etc/dbus-serialbattery/reinstalllocal.sh echo "make sure to disable Settings/Bluetooth in the Remote-Console to prevent reconnects every minute. In case of crash after ~12-16 hours disable raspberry pi 3 internal bluetooth via dtoverlay and use an external usb bluetooth-dongle" - +echo +echo "put your Bluetooth MAC adress in /data/etc/dbus-serialbattery/installble.sh and make sure to uncomment at least one install_service line..." diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 228d0bd6..88def822 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -15,7 +15,7 @@ def __init__(self, port, baud, address): self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) - logger.error("init of jkbmsble") + logger.error("init of jkbmsble at " + address) def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. @@ -24,7 +24,7 @@ def test_connection(self): # check if device with given mac is found, otherwise abort - logger.info("test of jkbmsble") + logger.info("test of jkbmsble at " + self.jk.address) try: loop = asyncio.get_event_loop() t = loop.create_task(BleakScanner.discover()) diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 3d4e080f..14eade66 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -24,4 +24,4 @@ if [ ! -f $filename ]; then chmod 755 $filename fi grep -qxF "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" $filename || echo "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" >> $filename - +grep -qxF "sh /data/etc/$DRIVERNAME/installble.sh" $filename || echo "sh /data/etc/$DRIVERNAME/installble.sh" >> $filename diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index bb6eaea9..32cc8f10 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -18,7 +18,6 @@ {"bms": "Daly", "address": b"\x40"}, {"bms": "Daly", "address": b"\x80"}, {"bms": "Jkbms", "baud": 115200}, - {"bms": "Jkbms_Ble", "address": "C8:47:8C:E4:54:0E"}, # {"bms" : "Sinowealth"}, {"bms": "Lifepower"}, {"bms": "Renogy", "address": b"\x30"}, From 79f82f072b3b8f37cb6af2fccb3f53eda7bcaab1 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Wed, 29 Mar 2023 06:24:48 +0000 Subject: [PATCH 050/209] add disconnect before starting the service --- etc/dbus-serialbattery/installble.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 19dd2ad2..0a463e0b 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -4,6 +4,7 @@ install_service() { mkdir -p /service/dbus-blebattery-$1 echo '#!/bin/sh' > /service/dbus-blebattery-$1/run + echo 'bluetoothctl disconnect ' "$3" >> /service/dbus-blebattery-$1/run echo 'python /data/etc/dbus-serialbattery/dbus-serialbattery.py ' "$2" "$3" >> /service/dbus-blebattery-$1/run chmod 755 /service/dbus-blebattery-$1/run } From 2a65d6c0fbcea030d52b6d0854a8b6536fa26900 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Wed, 29 Mar 2023 06:35:57 +0000 Subject: [PATCH 051/209] unique id per BLE device --- etc/dbus-serialbattery/jkbms_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 88def822..5b59edc5 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -11,7 +11,7 @@ class Jkbms_Ble(Battery): BATTERYTYPE = "Jkbms BLE" def __init__(self, port, baud, address): - super(Jkbms_Ble, self).__init__("zero", baud) + super(Jkbms_Ble, self).__init__(address.replace(":", "").lower(), baud) self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) From 0a2adb267cc7ce5398fe966d5f2215e050d38858 Mon Sep 17 00:00:00 2001 From: Stefan Seidel Date: Wed, 29 Mar 2023 15:41:40 +0000 Subject: [PATCH 052/209] enable logging and improve error messages --- etc/dbus-serialbattery/installble.sh | 12 ++++++++---- etc/dbus-serialbattery/jkbms_ble.py | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 0a463e0b..442d86bd 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -2,10 +2,14 @@ ## DO NOT TOUCH THIS ## install_service() { - mkdir -p /service/dbus-blebattery-$1 - echo '#!/bin/sh' > /service/dbus-blebattery-$1/run - echo 'bluetoothctl disconnect ' "$3" >> /service/dbus-blebattery-$1/run - echo 'python /data/etc/dbus-serialbattery/dbus-serialbattery.py ' "$2" "$3" >> /service/dbus-blebattery-$1/run + mkdir -p /service/dbus-blebattery-$1/log + echo "#!/bin/sh" > /service/dbus-blebattery-$1/log/run + echo "exec multilog t s25000 n4 /var/log/dbus-blebattery-$1" >> /service/dbus-blebattery-$1/log/run + chmod 755 /service/dbus-blebattery-$1/log/run + echo "#!/bin/sh" > /service/dbus-blebattery-$1/run + echo "exec 2>&1" >> /service/dbus-blebattery-$1/run + echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery-$1/run + echo "python /data/etc/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery-$1/run chmod 755 /service/dbus-blebattery-$1/run } ## END DO NOT TOUCH AREA ## diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 5b59edc5..26bd3f7a 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -15,7 +15,7 @@ def __init__(self, port, baud, address): self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) - logger.error("init of jkbmsble at " + address) + logger.info("init of jkbmsble at " + address) def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. @@ -38,6 +38,7 @@ def test_connection(self): if d.address == self.jk.address: found = True if not found: + logger.error("no BMS found at " + self.jk.address) return False # device was found, presumeably a jkbms so start scraping From 5f3fc3400eaadec69a19d29205c3c0758e2d83f8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 13 Apr 2023 11:30:07 +0200 Subject: [PATCH 053/209] Update jkbms_ble from master --- .flake8 | 4 +- .gitattributes | 5 + .github/workflows/analyse.yml | 32 +- .github/workflows/release.yml | 31 + .gitignore | 4 + README.md | 21 + buildfiles.lst | 32 -- create_build.sh | 4 - etc/dbus-serialbattery/ant.py | 4 +- etc/dbus-serialbattery/battery.py | 534 +++++++++++------- etc/dbus-serialbattery/battery_template.py | 4 +- etc/dbus-serialbattery/config.default.ini | 186 ++++++ etc/dbus-serialbattery/daly.py | 2 +- etc/dbus-serialbattery/dbus-serialbattery.py | 63 ++- etc/dbus-serialbattery/dbushelper.py | 110 ++-- etc/dbus-serialbattery/disabledriver.sh | 20 +- etc/dbus-serialbattery/ecs.py | 4 +- etc/dbus-serialbattery/installble.sh | 4 +- etc/dbus-serialbattery/installlocal.sh | 1 + etc/dbus-serialbattery/installqml.sh | 43 +- etc/dbus-serialbattery/installrelease.sh | 5 +- etc/dbus-serialbattery/jkbms.py | 69 ++- etc/dbus-serialbattery/lifepower.py | 53 +- etc/dbus-serialbattery/lltjbd.py | 4 +- etc/dbus-serialbattery/mnb.py | 2 +- etc/dbus-serialbattery/reinstalllocal.sh | 17 +- etc/dbus-serialbattery/renogy.py | 2 +- etc/dbus-serialbattery/restoregui.sh | 4 +- etc/dbus-serialbattery/revov.py | 4 +- etc/dbus-serialbattery/sinowealth.py | 4 +- etc/dbus-serialbattery/start-serialbattery.sh | 2 +- etc/dbus-serialbattery/uninstall.sh | 24 + etc/dbus-serialbattery/utils.py | 375 +++++++----- requirements.txt | 1 - 34 files changed, 1137 insertions(+), 537 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/release.yml delete mode 100644 buildfiles.lst delete mode 100644 create_build.sh create mode 100644 etc/dbus-serialbattery/config.default.ini create mode 100644 etc/dbus-serialbattery/uninstall.sh diff --git a/.flake8 b/.flake8 index 028e4537..5373f0c2 100644 --- a/.flake8 +++ b/.flake8 @@ -2,7 +2,6 @@ max-line-length = 120 exclude = ./etc/dbus-serialbattery/ant.py, - ./etc/dbus-serialbattery/battery.py, ./etc/dbus-serialbattery/battery_template.py, ./etc/dbus-serialbattery/daly.py, ./etc/dbus-serialbattery/dbus-serialbattery.py, @@ -17,7 +16,8 @@ exclude = ./etc/dbus-serialbattery/sinowealth.py, ./etc/dbus-serialbattery/test_max17853.py, ./etc/dbus-serialbattery/util_max17853.py, + ./velib_python venv extend-ignore: - # E203 whitespace fefore ':' conflicts with black code formatting. Will be ignored in flake8 + # E203 whitespace before ':' conflicts with black code formatting. Will be ignored in flake8 E203 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bf6a83b3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf +*.{sh,[sS][hH]} text eol=lf +*.{pyb} text eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml index b1bacf16..8d027e13 100644 --- a/.github/workflows/analyse.yml +++ b/.github/workflows/analyse.yml @@ -6,34 +6,28 @@ on: - '**' jobs: - CodeQL: + analyse: name: Analyze Using GitHub CodeQL runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - + - name: Set up Python environment + uses: actions/setup-python@v4 + with: + python-version: "3.8.13" + - name: Clone velib_python and add it to PYTHONPATH for subsequent steps + run: | + git clone https://github.com/victronenergy/velib_python.git + echo PYTHONPATH=$PYTHONPATH:$(pwd)/velib_python >> $GITHUB_ENV - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: python - - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 - lint: - name: Check Code Formatting - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Execute black lint check - uses: psf/black@stable - - - name: Set up Python environment - uses: actions/setup-python@v4 - with: - python-version: "3.11" - - name: flake8 Lint - uses: py-actions/flake8@v2 + - name: Execute black lint check + uses: psf/black@stable + - name: flake8 Lint + uses: py-actions/flake8@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..c54513bb --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + push: + tags: + - "v*.*.*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: build release archive + run: | + find . -type f -name "*.py" -exec chmod +x {} \; + find . -type f -name "*.sh" -exec chmod +x {} \; + find . -type f -name "run" -exec chmod +x {} \; + tar -czvf venus-data.tar.gz \ + --mode='a+rwX' \ + --exclude battery_template.py \ + --exclude __pycache__ \ + --exclude restartservice.sh \ + --exclude revov.py \ + --exclude test_max17853.py \ + conf/serial-starter.d \ + etc/dbus-serialbattery/ + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: venus-data.tar.gz diff --git a/.gitignore b/.gitignore index 83a2779d..287a9c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,7 @@ BMS-trials .project .pydevproject *.prefs +etc/dbus-serialbattery/config.ini + +# Local Clone of velib_python +velib_python \ No newline at end of file diff --git a/README.md b/README.md index c9e744a1..36bd7163 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,24 @@ The driver will communicate with a Battery Management System (BMS) that support If you find this driver helpful please considder supporting this project. You can buy me a Ko-Fi or get in contact if you would like to donate hardware. [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z73LCW1) or using [Paypal.me](https://paypal.me/innernet) + +### Developer Remarks +To develop this project, install the requirements. This project makes use of velib_python which is pre-installed on +Venus-OS Devices under `/opt/victronenergy/dbus-systemcalc-py/ext/velib_python`. To use the python files locally, +`git clone` the [velib_python](https://github.com/victronenergy/velib_python) project to velib_python and add +velib_python to the `PYTHONPATH` environment variable. + +#### How it works +* Each supported BMS needs to implement the abstract base class `Battery` from `battery.py`. +* `dbus-serialbattery.py` tries to figure out the correct connected BMS by looping through all known implementations of +`Battery` and executing its `test_connection()`. If this returns true, `dbus-serialbattery.py` sticks with this battery +and then periodically executes `dbushelpert.publish_battery()`. `publish_battery()` executes `Battery.refresh_data()` which +updates the fields of Battery. It then publishes those fields to dbus using `dbushelper.publish_dbus()` +* The Victron Device will be "controlled" by the values published on `/Info/` - namely: + * `/Info/MaxChargeCurrent ` + * `/Info/MaxDischargeCurrent` + * `/Info/MaxChargeVoltage` + * `/Info/BatteryLowVoltage` + * `/Info/ChargeRequest` (not implemented in dbus-serialbattery) + +For more details on the victron dbus interface see [the official victron dbus documentation](https://github.com/victronenergy/venus/wiki/dbus) diff --git a/buildfiles.lst b/buildfiles.lst deleted file mode 100644 index fe7809b0..00000000 --- a/buildfiles.lst +++ /dev/null @@ -1,32 +0,0 @@ -conf/serial-starter.d -etc/dbus-serialbattery/service/run -etc/dbus-serialbattery/service/log/run -etc/dbus-serialbattery/LICENSE -etc/dbus-serialbattery/README.md -etc/dbus-serialbattery/start-serialbattery.sh -etc/dbus-serialbattery/disabledriver.sh -etc/dbus-serialbattery/installlocal.sh -etc/dbus-serialbattery/installrelease.sh -etc/dbus-serialbattery/reinstalllocal.sh -etc/dbus-serialbattery/restoregui.sh -etc/dbus-serialbattery/installqml.sh -etc/dbus-serialbattery/qml/PageBattery.qml -etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml -etc/dbus-serialbattery/qml/PageBatterySetup.qml -etc/dbus-serialbattery/minimalmodbus.py -etc/dbus-serialbattery/dbus-serialbattery.py -etc/dbus-serialbattery/dbushelper.py -etc/dbus-serialbattery/battery.py -etc/dbus-serialbattery/utils.py -etc/dbus-serialbattery/lltjbd.py -etc/dbus-serialbattery/daly.py -etc/dbus-serialbattery/ant.py -etc/dbus-serialbattery/util_max17853.py -etc/dbus-serialbattery/mnb.py -etc/dbus-serialbattery/jkbms.py -etc/dbus-serialbattery/jkbms_ble.py -etc/dbus-serialbattery/jkbms_brn.py -etc/dbus-serialbattery/sinowealth.py -etc/dbus-serialbattery/renogy.py -etc/dbus-serialbattery/ecs.py -etc/dbus-serialbattery/lifepower.py diff --git a/create_build.sh b/create_build.sh deleted file mode 100644 index 55d48fe9..00000000 --- a/create_build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -dos2unix buildfiles.lst -cat buildfiles.lst | xargs dos2unix -tar -czvf venus-data.tar.gz --mode='a+rwX' -T buildfiles.lst diff --git a/etc/dbus-serialbattery/ant.py b/etc/dbus-serialbattery/ant.py index 215a692f..07b58d11 100644 --- a/etc/dbus-serialbattery/ant.py +++ b/etc/dbus-serialbattery/ant.py @@ -5,8 +5,8 @@ class Ant(Battery): - def __init__(self, port, baud): - super(Ant, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Ant, self).__init__(port, baud, address) self.type = self.BATTERYTYPE command_general = b"\xDB\xDB\x00\x00\x00\x00" diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 28ac2412..5abce0b1 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -1,28 +1,40 @@ # -*- coding: utf-8 -*- -from utils import * +from typing import Union, Tuple, List + +from utils import logger +import utils +import logging import math -from datetime import timedelta from time import time +from abc import ABC, abstractmethod class Protection(object): - # 2 = Alarm, 1 = Warning, 0 = Normal + """ + This class holds Warning and alarm states for different types of Checks + They are of type integer, 2 represents an Alarm, 1 a Warning, 0 if everything is fine + """ + def __init__(self): - self.voltage_high = None - self.voltage_low = None - self.voltage_cell_low = None - self.soc_low = None - self.current_over = None - self.current_under = None - self.cell_imbalance = None - self.internal_failure = None - self.temp_high_charge = None - self.temp_low_charge = None - self.temp_high_discharge = None - self.temp_low_discharge = None + self.voltage_high: int = None + self.voltage_low: int = None + self.voltage_cell_low: int = None + self.soc_low: int = None + self.current_over: int = None + self.current_under: int = None + self.cell_imbalance: int = None + self.internal_failure: int = None + self.temp_high_charge: int = None + self.temp_low_charge: int = None + self.temp_high_discharge: int = None + self.temp_low_discharge: int = None class Cell: + """ + This class holds information about a single Cell + """ + voltage = None balance = None temp = None @@ -31,8 +43,14 @@ def __init__(self, balance): self.balance = balance -class Battery(object): - def __init__(self, port, baud): +class Battery(ABC): + """ + This Class is the abstract baseclass for all batteries. For each BMS this class needs to be extended + and the abstract methods need to be implemented. The main program in dbus-serialbattery.py will then + use the individual implementations as type Battery and work with it. + """ + + def __init__(self, port, baud, address): self.port = port self.baud_rate = baud self.role = "battery" @@ -51,6 +69,7 @@ def __init__(self, port, baud): self.protection = Protection() self.version = None self.soc = None + self.time_to_soc_update = 0 self.charge_fet = None self.discharge_fet = None self.balance_fet = None @@ -59,10 +78,11 @@ def __init__(self, port, baud): self.temp1 = None self.temp2 = None self.temp_mos = None - self.cells = [] + self.cells: List[Cell] = [] self.control_charging = None self.control_voltage = None self.allow_max_voltage = True + self.control_voltage_last_set = 0 self.max_voltage_start_time = None self.control_current = None self.control_previous_total = None @@ -75,30 +95,48 @@ def __init__(self, port, baud): self.max_battery_charge_current = None self.max_battery_discharge_current = None - self.time_to_soc_update = TIME_TO_SOC_LOOP_CYCLES - - def test_connection(self): + @abstractmethod + def test_connection(self) -> bool: + """ + This abstract method needs to be implemented for each BMS. It shoudl return true if a connection + to the BMS can be established, false otherwise. + :return: the success state + """ # Each driver must override this function to test if a connection can be made - # return false when fail, true if successful + # return false when failed, true if successful return False - def get_settings(self): - # Each driver must override this function to read/set the battery settings - # It is called once after a successful connection by DbusHelper.setup_vedbus() - # Values: battery_type, version, hardware_version, min_battery_voltage, max_battery_voltage, - # MAX_BATTERY_CHARGE_CURRENT, MAX_BATTERY_DISCHARGE_CURRENT, cell_count, capacity - # return false when fail, true if successful + @abstractmethod + def get_settings(self) -> bool: + """ + Each driver must override this function to read/set the battery settings + It is called once after a successful connection by DbusHelper.setup_vedbus() + Values: battery_type, version, hardware_version, min_battery_voltage, max_battery_voltage, + MAX_BATTERY_CHARGE_CURRENT, MAX_BATTERY_DISCHARGE_CURRENT, cell_count, capacity + + :return: false when fail, true if successful + """ return False - def refresh_data(self): - # Each driver must override this function to read battery data and populate this class - # It is called each poll just before the data is published to vedbus - # return false when fail, true if successful + @abstractmethod + def refresh_data(self) -> bool: + """ + Each driver must override this function to read battery data and populate this class + It is called each poll just before the data is published to vedbus + + :return: false when fail, true if successful + """ return False - def to_temp(self, sensor, value): - # Keep the temp value between -20 and 100 to handle sensor issues or no data. - # The BMS should have already protected before those limits have been reached. + def to_temp(self, sensor: int, value: float) -> None: + """ + Keep the temp value between -20 and 100 to handle sensor issues or no data. + The BMS should have already protected before those limits have been reached. + + :param sensor: temperature sensor number + :param value: the sensor value + :return: + """ if sensor == 1: self.temp1 = min(max(value, -20), 100) if sensor == 2: @@ -106,82 +144,144 @@ def to_temp(self, sensor, value): if sensor == 'mos': self.temp_mos = min(max(value, -20), 100) - def manage_charge_voltage(self): - if LINEAR_LIMITATION_ENABLE: + def manage_charge_voltage(self) -> None: + """ + manages the charge voltage by setting self.control_voltage + :return: None + """ + if utils.LINEAR_LIMITATION_ENABLE: self.manage_charge_voltage_linear() else: self.manage_charge_voltage_step() - def manage_charge_voltage_linear(self): + def manage_charge_voltage_linear(self) -> None: + """ + manages the charge voltage using linear interpolation by setting self.control_voltage + :return: None + """ foundHighCellVoltage = False - if CVCM_ENABLE: - currentBatteryVoltage = 0 - penaltySum = 0 + voltageSum = 0 + penaltySum = 0 + tDiff = 0 + if utils.CVCM_ENABLE: + # calculate battery sum for i in range(self.cell_count): - cv = self.get_cell_voltage(i) - if cv: - currentBatteryVoltage += cv + voltage = self.get_cell_voltage(i) + if voltage: + voltageSum += voltage - if cv >= PENALTY_AT_CELL_VOLTAGE[0]: + # calculate penalty sum to prevent single cell overcharge + if voltage >= utils.PENALTY_AT_CELL_VOLTAGE[0]: + # foundHighCellVoltage: reset to False is not needed, since it is recalculated every second foundHighCellVoltage = True - penaltySum += calcLinearRelationship( - cv, PENALTY_AT_CELL_VOLTAGE, PENALTY_BATTERY_VOLTAGE + penaltySum += utils.calcLinearRelationship( + voltage, + utils.PENALTY_AT_CELL_VOLTAGE, + utils.PENALTY_BATTERY_VOLTAGE, ) - self.voltage = currentBatteryVoltage # for testing - if foundHighCellVoltage: - # Keep penalty above min battery voltage - self.control_voltage = max( - currentBatteryVoltage - penaltySum, MIN_CELL_VOLTAGE * self.cell_count - ) + voltageSum = round(voltageSum, 3) + + if self.max_voltage_start_time is None: + if ( + utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + and self.allow_max_voltage + ): + self.max_voltage_start_time = time() + elif ( + utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + and not self.allow_max_voltage + ): + self.allow_max_voltage = True + else: + tDiff = time() - self.max_voltage_start_time + if utils.MAX_VOLTAGE_TIME_SEC < tDiff: + self.allow_max_voltage = False + self.max_voltage_start_time = None + + # INFO: battery will only switch to Absorption if all cells are balanced.reach MAC_CELL_VOLTAGE * cell count if they are all balanced. + if ( + foundHighCellVoltage + and self.allow_max_voltage + ): + # set CVL only once every PENALTY_RECALCULATE_EVERY seconds + control_voltage_time = int(time() / utils.PENALTY_RECALCULATE_EVERY) + if control_voltage_time != self.control_voltage_last_set: + # Keep penalty above min battery voltage + self.control_voltage = round( max( + voltageSum - penaltySum, + utils.MIN_CELL_VOLTAGE * self.cell_count, + ), 3) + self.control_voltage_last_set = control_voltage_time + + elif self.allow_max_voltage: + self.control_voltage = round( (utils.MAX_CELL_VOLTAGE * self.cell_count), 3) + else: - self.control_voltage = FLOAT_CELL_VOLTAGE * self.cell_count + self.control_voltage = round( (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3) - def manage_charge_voltage_step(self): + def manage_charge_voltage_step(self) -> None: + """ + manages the charge voltage using a step function by setting self.control_voltage + :return: None + """ voltageSum = 0 - if CVCM_ENABLE: + tDiff = 0 + if utils.CVCM_ENABLE: + + # calculate battery sum for i in range(self.cell_count): voltage = self.get_cell_voltage(i) if voltage: voltageSum += voltage - if None == self.max_voltage_start_time: + if self.max_voltage_start_time is None: + # check if max voltage is reached and start timer to keep max voltage if ( - MAX_CELL_VOLTAGE * self.cell_count <= voltageSum - and True == self.allow_max_voltage + utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + and self.allow_max_voltage ): + # example 2 self.max_voltage_start_time = time() + + # check if reset soc is greater than battery soc + # this prevents flapping between max and float voltage + elif ( + utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + and not self.allow_max_voltage + ): + self.allow_max_voltage = True + + # do nothing else: - if ( - SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc - and not self.allow_max_voltage - ): - self.allow_max_voltage = True + pass + + # timer started else: tDiff = time() - self.max_voltage_start_time - if MAX_VOLTAGE_TIME_SEC < tDiff: - self.max_voltage_start_time = None + if utils.MAX_VOLTAGE_TIME_SEC < tDiff: self.allow_max_voltage = False + self.max_voltage_start_time = None + + else: + pass if self.allow_max_voltage: - # Keep penalty above min battery voltage - self.control_voltage = max( - MAX_CELL_VOLTAGE * self.cell_count, MIN_CELL_VOLTAGE * self.cell_count - ) + self.control_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count else: - self.control_voltage = FLOAT_CELL_VOLTAGE * self.cell_count + self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count - def manage_charge_current(self): + def manage_charge_current(self) -> None: # Manage Charge Current Limitations charge_limits = [self.max_battery_charge_current] - if CCCM_SOC_ENABLE: + if utils.CCCM_SOC_ENABLE: charge_limits.append(self.calcMaxChargeCurrentReferringToSoc()) - if CCCM_CV_ENABLE: + if utils.CCCM_CV_ENABLE: charge_limits.append(self.calcMaxChargeCurrentReferringToCellVoltage()) - if CCCM_T_ENABLE: + if utils.CCCM_T_ENABLE: charge_limits.append(self.calcMaxChargeCurrentReferringToTemperature()) - self.control_charge_current = min(charge_limits) + self.control_charge_current = round( min(charge_limits), 3) if self.control_charge_current == 0: self.control_allow_charge = False @@ -190,145 +290,154 @@ def manage_charge_current(self): # Manage Discharge Current Limitations discharge_limits = [self.max_battery_discharge_current] - if DCCM_SOC_ENABLE: + if utils.DCCM_SOC_ENABLE: discharge_limits.append(self.calcMaxDischargeCurrentReferringToSoc()) - if DCCM_CV_ENABLE: + if utils.DCCM_CV_ENABLE: discharge_limits.append( self.calcMaxDischargeCurrentReferringToCellVoltage() ) - if DCCM_T_ENABLE: + if utils.DCCM_T_ENABLE: discharge_limits.append( self.calcMaxDischargeCurrentReferringToTemperature() ) - self.control_discharge_current = min(discharge_limits) + self.control_discharge_current = round( min(discharge_limits), 3) if self.control_discharge_current == 0: self.control_allow_discharge = False else: self.control_allow_discharge = True - def calcMaxChargeCurrentReferringToCellVoltage(self): + def calcMaxChargeCurrentReferringToCellVoltage(self) -> float: try: - if LINEAR_LIMITATION_ENABLE: - return calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + return utils.calcLinearRelationship( self.get_max_cell_voltage(), - CELL_VOLTAGES_WHILE_CHARGING, - MAX_CHARGE_CURRENT_CV, + utils.CELL_VOLTAGES_WHILE_CHARGING, + utils.MAX_CHARGE_CURRENT_CV, ) - return calcStepRelationship( + return utils.calcStepRelationship( self.get_max_cell_voltage(), - CELL_VOLTAGES_WHILE_CHARGING, - MAX_CHARGE_CURRENT_CV, + utils.CELL_VOLTAGES_WHILE_CHARGING, + utils.MAX_CHARGE_CURRENT_CV, False, ) - except: + except Exception: return self.max_battery_charge_current - def calcMaxDischargeCurrentReferringToCellVoltage(self): + def calcMaxDischargeCurrentReferringToCellVoltage(self) -> float: try: - if LINEAR_LIMITATION_ENABLE: - return calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + return utils.calcLinearRelationship( self.get_min_cell_voltage(), - CELL_VOLTAGES_WHILE_DISCHARGING, - MAX_DISCHARGE_CURRENT_CV, + utils.CELL_VOLTAGES_WHILE_DISCHARGING, + utils.MAX_DISCHARGE_CURRENT_CV, ) - return calcStepRelationship( + return utils.calcStepRelationship( self.get_min_cell_voltage(), - CELL_VOLTAGES_WHILE_DISCHARGING, - MAX_DISCHARGE_CURRENT_CV, + utils.CELL_VOLTAGES_WHILE_DISCHARGING, + utils.MAX_DISCHARGE_CURRENT_CV, True, ) - except: + except Exception: return self.max_battery_charge_current - def calcMaxChargeCurrentReferringToTemperature(self): + def calcMaxChargeCurrentReferringToTemperature(self) -> float: if self.get_max_temp() is None: return self.max_battery_charge_current temps = {0: self.get_max_temp(), 1: self.get_min_temp()} for key, currentMaxTemperature in temps.items(): - if LINEAR_LIMITATION_ENABLE: - temps[key] = calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + temps[key] = utils.calcLinearRelationship( currentMaxTemperature, - TEMPERATURE_LIMITS_WHILE_CHARGING, - MAX_CHARGE_CURRENT_T, + utils.TEMPERATURE_LIMITS_WHILE_CHARGING, + utils.MAX_CHARGE_CURRENT_T, ) else: - temps[key] = calcStepRelationship( + temps[key] = utils.calcStepRelationship( currentMaxTemperature, - TEMPERATURE_LIMITS_WHILE_CHARGING, - MAX_CHARGE_CURRENT_T, + utils.TEMPERATURE_LIMITS_WHILE_CHARGING, + utils.MAX_CHARGE_CURRENT_T, False, ) return min(temps[0], temps[1]) - def calcMaxDischargeCurrentReferringToTemperature(self): + def calcMaxDischargeCurrentReferringToTemperature(self) -> float: if self.get_max_temp() is None: return self.max_battery_discharge_current temps = {0: self.get_max_temp(), 1: self.get_min_temp()} for key, currentMaxTemperature in temps.items(): - if LINEAR_LIMITATION_ENABLE: - temps[key] = calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + temps[key] = utils.calcLinearRelationship( currentMaxTemperature, - TEMPERATURE_LIMITS_WHILE_DISCHARGING, - MAX_DISCHARGE_CURRENT_T, + utils.TEMPERATURE_LIMITS_WHILE_DISCHARGING, + utils.MAX_DISCHARGE_CURRENT_T, ) else: - temps[key] = calcStepRelationship( + temps[key] = utils.calcStepRelationship( currentMaxTemperature, - TEMPERATURE_LIMITS_WHILE_DISCHARGING, - MAX_DISCHARGE_CURRENT_T, + utils.TEMPERATURE_LIMITS_WHILE_DISCHARGING, + utils.MAX_DISCHARGE_CURRENT_T, True, ) return min(temps[0], temps[1]) - def calcMaxChargeCurrentReferringToSoc(self): + def calcMaxChargeCurrentReferringToSoc(self) -> float: try: # Create value list. Will more this to the settings object - SOC_WHILE_CHARGING = [100, CC_SOC_LIMIT1, CC_SOC_LIMIT2, CC_SOC_LIMIT3] + SOC_WHILE_CHARGING = [ + 100, + utils.CC_SOC_LIMIT1, + utils.CC_SOC_LIMIT2, + utils.CC_SOC_LIMIT3, + ] MAX_CHARGE_CURRENT_SOC = [ - CC_CURRENT_LIMIT1, - CC_CURRENT_LIMIT2, - CC_CURRENT_LIMIT3, - MAX_BATTERY_CHARGE_CURRENT, + utils.CC_CURRENT_LIMIT1, + utils.CC_CURRENT_LIMIT2, + utils.CC_CURRENT_LIMIT3, + utils.MAX_BATTERY_CHARGE_CURRENT, ] - if LINEAR_LIMITATION_ENABLE: - return calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + return utils.calcLinearRelationship( self.soc, SOC_WHILE_CHARGING, MAX_CHARGE_CURRENT_SOC ) - return calcStepRelationship( + return utils.calcStepRelationship( self.soc, SOC_WHILE_CHARGING, MAX_CHARGE_CURRENT_SOC, True ) - except: + except Exception: return self.max_battery_charge_current - def calcMaxDischargeCurrentReferringToSoc(self): + def calcMaxDischargeCurrentReferringToSoc(self) -> float: try: # Create value list. Will more this to the settings object - SOC_WHILE_DISCHARGING = [DC_SOC_LIMIT3, DC_SOC_LIMIT2, DC_SOC_LIMIT1] + SOC_WHILE_DISCHARGING = [ + utils.DC_SOC_LIMIT3, + utils.DC_SOC_LIMIT2, + utils.DC_SOC_LIMIT1, + ] MAX_DISCHARGE_CURRENT_SOC = [ - MAX_BATTERY_DISCHARGE_CURRENT, - DC_CURRENT_LIMIT3, - DC_CURRENT_LIMIT2, - DC_CURRENT_LIMIT1, + utils.MAX_BATTERY_DISCHARGE_CURRENT, + utils.DC_CURRENT_LIMIT3, + utils.DC_CURRENT_LIMIT2, + utils.DC_CURRENT_LIMIT1, ] - if LINEAR_LIMITATION_ENABLE: - return calcLinearRelationship( + if utils.LINEAR_LIMITATION_ENABLE: + return utils.calcLinearRelationship( self.soc, SOC_WHILE_DISCHARGING, MAX_DISCHARGE_CURRENT_SOC ) - return calcStepRelationship( + return utils.calcStepRelationship( self.soc, SOC_WHILE_DISCHARGING, MAX_DISCHARGE_CURRENT_SOC, True ) - except: + except Exception: return self.max_battery_charge_current - def get_min_cell(self): + def get_min_cell(self) -> int: min_voltage = 9999 min_cell = None if len(self.cells) == 0 and hasattr(self, "cell_min_no"): @@ -343,7 +452,7 @@ def get_min_cell(self): min_cell = c return min_cell - def get_max_cell(self): + def get_max_cell(self) -> int: max_voltage = 0 max_cell = None if len(self.cells) == 0 and hasattr(self, "cell_max_no"): @@ -358,56 +467,83 @@ def get_max_cell(self): max_cell = c return max_cell - def get_min_cell_desc(self): + def get_min_cell_desc(self) -> Union[str, None]: cell_no = self.get_min_cell() return cell_no if cell_no is None else "C" + str(cell_no + 1) - def get_max_cell_desc(self): + def get_max_cell_desc(self) -> Union[str, None]: cell_no = self.get_max_cell() return cell_no if cell_no is None else "C" + str(cell_no + 1) - def get_cell_voltage(self, idx): + def get_cell_voltage(self, idx) -> Union[float, None]: if idx >= min(len(self.cells), self.cell_count): return None return self.cells[idx].voltage - def get_cell_balancing(self, idx): + def get_cell_balancing(self, idx) -> Union[int, None]: if idx >= min(len(self.cells), self.cell_count): return None if self.cells[idx].balance is not None and self.cells[idx].balance: return 1 return 0 - def get_capacity_remain(self): + def get_capacity_remain(self) -> Union[float, None]: if self.capacity_remain is not None: return self.capacity_remain if self.capacity is not None and self.soc is not None: return self.capacity * self.soc / 100 return None - def get_timetosoc(self, socnum, crntPrctPerSec): + def get_timeToSoc(self, socnum, crntPrctPerSec, onlyNumber = False) -> str: if self.current > 0: diffSoc = socnum - self.soc else: diffSoc = self.soc - socnum ttgStr = None - if self.soc != socnum and (diffSoc > 0 or TIME_TO_SOC_INC_FROM is True): + if self.soc != socnum and (diffSoc > 0 or utils.TIME_TO_SOC_INC_FROM is True): secondstogo = int(diffSoc / crntPrctPerSec) ttgStr = "" - if TIME_TO_SOC_VALUE_TYPE & 1: + if onlyNumber or utils.TIME_TO_SOC_VALUE_TYPE & 1: ttgStr += str(secondstogo) - if TIME_TO_SOC_VALUE_TYPE & 2: + if not onlyNumber and utils.TIME_TO_SOC_VALUE_TYPE & 2: ttgStr += " [" - if TIME_TO_SOC_VALUE_TYPE & 2: - ttgStr += str(timedelta(seconds=secondstogo)) - if TIME_TO_SOC_VALUE_TYPE & 1: + if not onlyNumber and utils.TIME_TO_SOC_VALUE_TYPE & 2: + ttgStr += self.get_secondsToString(secondstogo) + + if utils.TIME_TO_SOC_VALUE_TYPE & 1: ttgStr += "]" return ttgStr - def get_min_cell_voltage(self): + def get_secondsToString(self, timespan, precision = 3) -> str: + """ + Transforms seconds to a string in the format: 1d 1h 1m 1s (Victron Style) + :param precision: + 0 = 1d + 1 = 1d 1h + 2 = 1d 1h 1m + 3 = 1d 1h 1m 1s + + This was added, since timedelta() returns strange values, if time is negative + e.g.: seconds: -70245 --> timedelta output: -1 day, 4:29:15 --> calculation: -1 day + 4:29:15 --> real value -19:30:45 + """ + tmp = '' if timespan >= 0 else '-' + timespan = abs(timespan) + + m, s = divmod(timespan, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + + tmp += ( ( str(d) + 'd ' ) if d > 0 else '' ) + tmp += ( ( str(h) + 'h ' ) if precision >= 1 and h > 0 else '' ) + tmp += ( ( str(m) + 'm ' ) if precision >= 2 and m > 0 else '' ) + tmp += ( ( str(s) + 's ' ) if precision == 3 and s > 0 else '' ) + + return tmp.rstrip() + + def get_min_cell_voltage(self) -> Union[float, None]: min_voltage = None if hasattr(self, "cell_min_voltage"): min_voltage = self.cell_min_voltage @@ -421,7 +557,7 @@ def get_min_cell_voltage(self): pass return min_voltage - def get_max_cell_voltage(self): + def get_max_cell_voltage(self) -> Union[float, None]: max_voltage = None if hasattr(self, "cell_max_voltage"): max_voltage = self.cell_max_voltage @@ -435,9 +571,15 @@ def get_max_cell_voltage(self): pass return max_voltage - def get_midvoltage(self): + def get_midvoltage(self) -> Tuple[Union[float, None], Union[float, None]]: + """ + This method returns the Voltage "in the middle of the battery" + as well as a deviation of an ideally balanced battery. It does so by calculating the sum of the first half + of the cells and adding 1/2 of the "middle cell" voltage (if it exists) + :return: a tuple of the voltage in the middle, as well as a percentage deviation (total_voltage / 2) + """ if ( - not MIDPOINT_ENABLE + not utils.MIDPOINT_ENABLE or self.cell_count is None or self.cell_count == 0 or self.cell_count < 4 @@ -446,30 +588,28 @@ def get_midvoltage(self): return None, None halfcount = int(math.floor(self.cell_count / 2)) + uneven_cells_offset = self.cell_count % 2 half1voltage = 0 half2voltage = 0 try: half1voltage = sum( - c.voltage for c in self.cells[:halfcount] if c.voltage is not None + cell.voltage + for cell in self.cells[:halfcount] + if cell.voltage is not None ) half2voltage = sum( - c.voltage - for c in self.cells[halfcount : halfcount * 2] - if c.voltage is not None + cell.voltage + for cell in self.cells[halfcount + uneven_cells_offset :] + if cell.voltage is not None ) except ValueError: pass try: - # handle uneven cells by giving half the voltage of the last cell to half1 and half2 - extra = ( - 0 - if (2 * halfcount == self.cell_count) - else self.cells[self.cell_count - 1].voltage / 2 - ) + extra = 0 if self.cell_count % 2 == 0 else self.cells[halfcount].voltage / 2 # get the midpoint of the battery - midpoint = (half1voltage + half2voltage) / 2 + extra + midpoint = half1voltage + extra return ( midpoint, (half2voltage - half1voltage) / (half2voltage + half1voltage) * 100, @@ -477,25 +617,15 @@ def get_midvoltage(self): except ValueError: return None, None - def get_balancing(self): + def get_balancing(self) -> int: for c in range(min(len(self.cells), self.cell_count)): if self.cells[c].balance is not None and self.cells[c].balance: return 1 return 0 - def get_temp(self): - if self.temp1 is not None and self.temp2 is not None: - return round((float(self.temp1) + float(self.temp2)) / 2, 2) - if self.temp1 is not None and self.temp2 is None: - return round(float(self.temp1), 2) - if self.temp1 is None and self.temp2 is not None: - return round(float(self.temp2), 2) - else: - return None - - def get_min_temp(self): + def extract_from_temp_values(self, extractor) -> Union[float, None]: if self.temp1 is not None and self.temp2 is not None: - return min(self.temp1, self.temp2) + return extractor(self.temp1, self.temp2) if self.temp1 is not None and self.temp2 is None: return self.temp1 if self.temp1 is None and self.temp2 is not None: @@ -503,23 +633,28 @@ def get_min_temp(self): else: return None - def get_max_temp(self): - if self.temp1 is not None and self.temp2 is not None: - return max(self.temp1, self.temp2) - if self.temp1 is not None and self.temp2 is None: - return self.temp1 - if self.temp1 is None and self.temp2 is not None: - return self.temp2 - else: - return None + def get_temp(self) -> Union[float, None]: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: round((float(temp1) + float(temp2)) / 2, 2) + ) - def get_mos_temp(self): + def get_min_temp(self) -> Union[float, None]: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: min(temp1, temp2) + ) + + def get_max_temp(self) -> Union[float, None]: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: max(temp1, temp2) + ) + + def get_mos_temp(self) -> Union[float, None]: if self.temp_mos is not None: return self.temp_mos else: return None - def log_cell_data(self): + def log_cell_data(self) -> bool: if logger.getEffectiveLevel() > logging.INFO and len(self.cells) == 0: return False @@ -531,20 +666,19 @@ def log_cell_data(self): logger.debug("Cells:" + cell_res) return True - def log_settings(self): + def log_settings(self) -> None: - logger.info(f"Battery {self.type} connected to dbus from {self.port}") - logger.info(f"=== Settings ===") cell_counter = len(self.cells) - logger.info( - f"> Connection voltage {self.voltage}V | current {self.current}A | SOC {self.soc}%" - ) - logger.info(f"> Cell count {self.cell_count} | cells populated {cell_counter}") - logger.info(f"> CCCM SOC {CCCM_SOC_ENABLE} | DCCM SOC {DCCM_SOC_ENABLE}") - logger.info(f"> CCCM CV {CCCM_CV_ENABLE} | DCCM CV {DCCM_CV_ENABLE}") - logger.info(f"> CCCM T {CCCM_T_ENABLE} | DCCM T {DCCM_T_ENABLE}") - logger.info( - f"> MIN_CELL_VOLTAGE {MIN_CELL_VOLTAGE}V | MAX_CELL_VOLTAGE {MAX_CELL_VOLTAGE}V" - ) + logger.info(f"Battery {self.type} connected to dbus from {self.port}") + logger.info( "========== Settings ==========") + logger.info(f"> Connection voltage: {self.voltage}V | Current: {self.current}A | SoC: {self.soc}%") + logger.info(f"> Cell count: {self.cell_count} | Cells populated: {cell_counter}") + logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") + logger.info(f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V") + logger.info(f"> CVCM: {utils.CVCM_ENABLE}") + logger.info(f"> MIN CELL VOLTAGE: {utils.MIN_CELL_VOLTAGE}V | MAX CELL VOLTAGE: {utils.MAX_CELL_VOLTAGE}V") + logger.info(f"> CCCM CV: {str(utils.CCCM_CV_ENABLE).ljust(5)} | DCCM CV: {utils.DCCM_CV_ENABLE}") + logger.info(f"> CCCM T: {str(utils.CCCM_T_ENABLE).ljust(5)} | DCCM T: {utils.DCCM_T_ENABLE}") + logger.info(f"> CCCM SOC: {str(utils.CCCM_SOC_ENABLE).ljust(5)} | DCCM SOC: {utils.DCCM_SOC_ENABLE}") return diff --git a/etc/dbus-serialbattery/battery_template.py b/etc/dbus-serialbattery/battery_template.py index b61546d6..e921efc0 100644 --- a/etc/dbus-serialbattery/battery_template.py +++ b/etc/dbus-serialbattery/battery_template.py @@ -5,8 +5,8 @@ class BatteryTemplate(Battery): - def __init__(self, port, baud): - super(BatteryTemplate, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(BatteryTemplate, self).__init__(port, baud, address) self.type = self.BATTERYTYPE BATTERYTYPE = "Template" diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini new file mode 100644 index 00000000..7bc2d501 --- /dev/null +++ b/etc/dbus-serialbattery/config.default.ini @@ -0,0 +1,186 @@ +[DEFAULT] + +; Choose the mode for voltage / current limitations (True / False) +; False is a step mode. This is the default with limitations on hard boundary steps +; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) +; For CVL the penalties are only applied, if the cell voltage reaches the penalty voltage +LINEAR_LIMITATION_ENABLE = False + +; Battery Current limits +MAX_BATTERY_CHARGE_CURRENT = 70.0 +MAX_BATTERY_DISCHARGE_CURRENT = 90.0 + + +; --------- Charge Voltage limitation (affecting CVL) --------- +; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) +; after max voltage is reached for MAX_VOLTAGE_TIME_SEC. It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT +; If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied +; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. +; Allow max voltage of 55.2V again, if SoC is once below 90% +; OR +; The battery reached max voltage of 55.2V and the max cell difference is 0.01V, then switch to float voltage of 53.6V to don't stress the batteries. +; Allow max voltage of 55.2V again if max cell difference is above 0.02V +; Charge voltage control management enable (True/False). +CVCM_ENABLE = False + +; -- Cell Voltages +; Description: Cell min/max voltages which are used to calculate the min/max battery voltage +; Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage +MIN_CELL_VOLTAGE = 2.90 +; Max voltage can seen as absorption voltage +MAX_CELL_VOLTAGE = 3.45 +FLOAT_CELL_VOLTAGE = 3.35 + +; -- Penalty Voltages +; NOTE: works only when LINEAR_LIMITATION_ENABLE = True +; More details can be found here: https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635 +; If the cell voltage reaches 3.48V, then reduce actual battery-voltage by 0.01V +; If the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded +; There will be a sum of all penalties for each cell, which exceeds the limits +; NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of PENALTY_BATTERY_VOLTAGE, +; else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. +PENALTY_AT_CELL_VOLTAGE = 3.48, 3.55, 3.6 +; this voltage will be subtracted +PENALTY_BATTERY_VOLTAGE = 0.01, 1.0, 2.0 +; Specify in seconds how often the penalty should be recalculated +PENALTY_RECALCULATE_EVERY = 60 + +; -- CVL Reset based on SoC option +; Reset max voltage after +MAX_VOLTAGE_TIME_SEC = 900 +; Specify SoC where CVL limit is reset to max voltage +SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90 + + +; --------- Cell Voltage Current limitation (affecting CCL/DCL) --------- +; Description: Maximal charge / discharge current will be in-/decreased depending on min and max cell voltages +; Example: 18 cells * 3.55V/cell = 63.9V max charge voltage +; 18 cells * 2.70V/cell = 48.6V min discharge voltage +; But in reality not all cells reach the same voltage at the same time. The (dis)charge current +; will be (in-/)decreased, if even ONE SINGLE BATTERY CELL reaches the limits + +; Charge current control management referring to cell-voltage enable (True/False). +CCCM_CV_ENABLE = True +; Discharge current control management referring to cell-voltage enable (True/False). +DCCM_CV_ENABLE = True + +; Set steps to reduce battery current +; The current will be changed linear between those steps if LINEAR_LIMITATION_ENABLE is set to True +CELL_VOLTAGES_WHILE_CHARGING = 3.55, 3.50, 3.45, 3.30 +MAX_CHARGE_CURRENT_CV_FRACTION = 0, 0.05, 0.5, 1 + +CELL_VOLTAGES_WHILE_DISCHARGING = 2.70, 2.80, 2.90, 3.10 +MAX_DISCHARGE_CURRENT_CV_FRACTION = 0, 0.1, 0.5, 1 + + +; --------- Temperature limitation (affecting CCL/DCL) --------- +; Description: Maximal charge / discharge current will be in-/decreased depending on temperature +; Example: The temperature limit will be monitored to control the currents. If there are two temperature senors, +; then the worst case will be calculated and the more secure lower current will be set. +; Charge current control management referring to temperature enable (True/False). +CCCM_T_ENABLE = True +; Charge current control management referring to temperature enable (True/False). +DCCM_T_ENABLE = True + +; Set steps to reduce battery current +; The current will be changed linear between those steps if LINEAR_LIMITATION_ENABLE is set to True +TEMPERATURE_LIMITS_WHILE_CHARGING = 0, 2, 5, 10, 15, 20, 35, 40, 55 +MAX_CHARGE_CURRENT_T_FRACTION = 0, 0.1, 0.2, 0.4, 0.8, 1, 1, 0.4, 0 + +TEMPERATURE_LIMITS_WHILE_DISCHARGING = -20, 0, 5, 10, 15, 45, 55 +MAX_DISCHARGE_CURRENT_T_FRACTION = 0, 0.2, 0.3, 0.4, 1, 1, 0 + + +; --------- SOC limitation (affecting CCL/DCL) --------- +; Description: Maximal charge / discharge current will be increased / decreased depending on State of Charge, +; see CC_SOC_LIMIT1 etc. +; Example: The SoC limit will be monitored to control the currents. +; Charge current control management enable (True/False). +CCCM_SOC_ENABLE = True +; Discharge current control management enable (True/False). +DCCM_SOC_ENABLE = True + +; Charge current soc limits +CC_SOC_LIMIT1 = 98 +CC_SOC_LIMIT2 = 95 +CC_SOC_LIMIT3 = 91 + +; Charge current limits +CC_CURRENT_LIMIT1_FRACTION = 0.1 +CC_CURRENT_LIMIT2_FRACTION = 0.3 +CC_CURRENT_LIMIT3_FRACTION = 0.5 + +; Discharge current soc limits +DC_SOC_LIMIT1 = 10 +DC_SOC_LIMIT2 = 20 +DC_SOC_LIMIT3 = 30 + +; Discharge current limits +DC_CURRENT_LIMIT1_FRACTION = 0.1 +DC_CURRENT_LIMIT2_FRACTION = 0.3 +DC_CURRENT_LIMIT3_FRACTION = 0.5 + + +; --------- Time-To-Soc --------- +; Description: Calculates the time to a specific SoC +; Example: TIME_TO_SOC_POINTS = 50, 25, 15, 0 +; 6h 24m remaining until 50% SoC +; 17h 36m remaining until 25% SoC +; 22h 5m remaining until 15% SoC +; 28h 48m remaining until 0% SoC +; Set of SoC percentages to report on dbus and MQTT. The more you specify the more it will impact system performance. +; [Valid values 0-100, comma separated list. More that 20 intervals are not recommended] +; Example: TIME_TO_SOC_POINTS = 100, 95, 90, 85, 75, 50, 25, 20, 10, 0 +; Leave empty to disable +TIME_TO_SOC_POINTS = +; Specify TimeToSoc value type [Valid values 1, 2, 3] +; 1 Seconds +; 2 Time string d h m s +; 3 Both seconds and time string " [d h m s]" +TIME_TO_SOC_VALUE_TYPE = 1 +; Specify in seconds how often the TimeToSoc should be recalculated +; Minimum are 5 seconds to prevent CPU overload +TIME_TO_SOC_RECALCULATE_EVERY = 60 +; Include TimeToSoC points when moving away from the SoC point [Valid values True, False] +; These will be as negative time. Disabling this improves performance slightly +TIME_TO_SOC_INC_FROM = False + + +; --------- Additional settings --------- +; Specify only one BMS type to load else leave empty to try to load all availabe +; LltJbd, Ant, Daly, Daly, Jkbms, Lifepower, Renogy, Renogy, Ecs +BMS_TYPE = + +; Publish the config settings to the dbus path "/Info/Config/" +PUBLISH_CONFIG_VALUES = 1 + +; Select the format of cell data presented on dbus [Valid values 0,1,2,3] +; 0 Do not publish all the cells (only the min/max cell data as used by the default GX) +; 1 Format: /Voltages/Cell (also available for display on Remote Console) +; 2 Format: /Cell/#/Volts +; 3 Both formats 1 and 2 +BATTERY_CELL_DATA_FORMAT = 1 + +; Simulate Midpoint graph (True/False). +MIDPOINT_ENABLE = False + + +; --------- BMS specific settings --------- + +; -- LltJbd settings +; SoC low levels +; NOTE: SOC_LOW_WARNING is also used to calculate the Time-To-Go even if you are not using a LltJbd BMS +SOC_LOW_WARNING = 20 +SOC_LOW_ALARM = 10 + +; -- Daly settings +; Battery capacity (amps) if the BMS does not support reading it +BATTERY_CAPACITY = 50 +; Invert Battery Current. Default non-inverted. Set to -1 to invert +INVERT_CURRENT_MEASUREMENT = 1 + +; -- ESC GreenMeter and Lipro device settings +GREENMETER_ADDRESS = 1 +LIPRO_START_ADDRESS = 2 +LIPRO_END_ADDRESS = 4 +LIPRO_CELL_COUNT = 15 diff --git a/etc/dbus-serialbattery/daly.py b/etc/dbus-serialbattery/daly.py index 10281cbd..ae96c723 100644 --- a/etc/dbus-serialbattery/daly.py +++ b/etc/dbus-serialbattery/daly.py @@ -6,7 +6,7 @@ class Daly(Battery): def __init__(self, port, baud, address): - super(Daly, self).__init__(port, baud) + super(Daly, self).__init__(port, baud, address) self.charger_connected = None self.load_connected = None self.command_address = address diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 1f07858e..59c0bb5c 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -1,9 +1,10 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +from typing import Union + from time import sleep from dbus.mainloop.glib import DBusGMainLoop from threading import Thread -import dbus import sys if sys.version_info.major == 2: @@ -15,20 +16,36 @@ # from ve_utils import exit_on_error from dbushelper import DbusHelper -from utils import DRIVER_VERSION, DRIVER_SUBVERSION, logger, battery_types -import logging +from utils import logger +import utils +from battery import Battery from lltjbd import LltJbd from daly import Daly from ant import Ant from jkbms import Jkbms -from jkbms_ble import Jkbms_Ble -from sinowealth import Sinowealth + +# from sinowealth import Sinowealth from renogy import Renogy from ecs import Ecs from lifepower import Lifepower -# from mnb import MNB - +supported_bms_types = [ + {"bms": LltJbd, "baud": 9600}, + {"bms": Ant, "baud": 19200}, + {"bms": Daly, "baud": 9600, "address": b"\x40"}, + {"bms": Daly, "baud": 9600, "address": b"\x80"}, + {"bms": Jkbms, "baud": 115200}, + # {"bms" : Sinowealth}, + {"bms": Lifepower, "baud": 9600}, + {"bms": Renogy, "baud": 9600, "address": b"\x30"}, + {"bms": Renogy, "baud": 9600, "address": b"\xF7"}, + {"bms": Ecs, "baud": 19200}, +] +expected_bms_types = [ + battery_type + for battery_type in supported_bms_types + if battery_type["bms"].__name__ == utils.BMS_TYPE or utils.BMS_TYPE == "" +] logger.info("Starting dbus-serialbattery") @@ -42,34 +59,30 @@ def poll_battery(loop): poller.start() return True - def get_battery_type(_port): + def get_battery(_port) -> Union[Battery, None]: # all the different batteries the driver support and need to test for # try to establish communications with the battery 3 times, else exit count = 3 while count > 0: # create a new battery object that can read the battery and run connection test - for test in battery_types: - logger.info("Testing " + test["bms"]) - class_ = eval(test["bms"]) - if "baud" in test.keys(): - baud = test["baud"] - else: - baud = 9600 - if "address" in test.keys(): - testbms = class_(_port, baud, test["address"]) - else: - testbms = class_(_port, baud) - if testbms.test_connection() is True: + for test in expected_bms_types: + logger.info("Testing " + test["bms"].__name__) + batteryClass = test["bms"] + baud = test["baud"] + battery: Battery = batteryClass( + port=_port, baud=baud, address=test.get("address") + ) + if battery.test_connection(): logger.info( - "Connection established to " + testbms.__class__.__name__ + "Connection established to " + battery.__class__.__name__ ) - return testbms + return battery count -= 1 sleep(0.5) return None - def get_port(): + def get_port() -> str: # Get the port we need to use from the argument if len(sys.argv) > 1: return sys.argv[1] @@ -78,7 +91,9 @@ def get_port(): logger.info("No Port needed") return "/dev/tty/USB9" - logger.info("dbus-serialbattery v" + str(DRIVER_VERSION) + DRIVER_SUBVERSION) + logger.info( + "dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION + ) port = get_port() battery = None diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 35b0512e..6cfc3260 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -4,6 +4,7 @@ import platform import dbus import traceback +from time import time # Victron packages sys.path.insert( @@ -296,9 +297,21 @@ def setup_vedbus(self): gettextcallback=lambda p, v: "{:0.3f}V".format(v), ) - # Create TimeToSoC items - for num in TIME_TO_SOC_POINTS: - self._dbusservice.add_path("/TimeToSoC/" + str(num), None, writeable=True) + # Create TimeToSoC items only if enabled + if ( + self.battery.capacity is not None + and len(TIME_TO_SOC_POINTS) > 0 + ): + # Create TimeToGo item + self._dbusservice.add_path("/TimeToGo", None, writeable=True) + + # Create TimeToSoc items + for num in TIME_TO_SOC_POINTS: + self._dbusservice.add_path("/TimeToSoC/" + str(num), None, writeable=True) + + logger.info(f"publish config values = {PUBLISH_CONFIG_VALUES}") + if PUBLISH_CONFIG_VALUES == 1: + publish_config_variables(self._dbusservice) return True @@ -325,7 +338,7 @@ def publish_battery(self, loop): # This is to mannage CVCL self.battery.manage_charge_voltage() - # publish all the data fro the battery object to dbus + # publish all the data from the battery object to dbus self.publish_dbus() except: @@ -345,7 +358,7 @@ def publish_dbus(self): self._dbusservice["/Dc/0/Temperature"] = self.battery.get_temp() self._dbusservice["/Capacity"] = self.battery.get_capacity_remain() self._dbusservice["/ConsumedAmphours"] = ( - 0 + None if self.battery.capacity is None or self.battery.get_capacity_remain() is None else self.battery.capacity - self.battery.get_capacity_remain() @@ -363,7 +376,9 @@ def publish_dbus(self): 1 if self.battery.charge_fet and self.battery.control_allow_charge else 0 ) self._dbusservice["/Io/AllowToDischarge"] = ( - 1 if self.battery.discharge_fet else 0 + 1 + if self.battery.discharge_fet and self.battery.control_allow_discharge + else 0 ) self._dbusservice["/Io/AllowToBalance"] = ( 1 if self.battery.balance_fet else 0 @@ -386,12 +401,8 @@ def publish_dbus(self): self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control - self._dbusservice[ - "/Info/MaxChargeCurrent" - ] = self.battery.control_charge_current - self._dbusservice[ - "/Info/MaxDischargeCurrent" - ] = self.battery.control_discharge_current + self._dbusservice["/Info/MaxChargeCurrent"] = self.battery.control_charge_current + self._dbusservice["/Info/MaxDischargeCurrent"] = self.battery.control_discharge_current # Voltage control self._dbusservice["/Info/MaxChargeVoltage"] = self.battery.control_voltage @@ -399,45 +410,23 @@ def publish_dbus(self): # Updates from cells self._dbusservice["/System/MinVoltageCellId"] = self.battery.get_min_cell_desc() self._dbusservice["/System/MaxVoltageCellId"] = self.battery.get_max_cell_desc() - self._dbusservice[ - "/System/MinCellVoltage" - ] = self.battery.get_min_cell_voltage() - self._dbusservice[ - "/System/MaxCellVoltage" - ] = self.battery.get_max_cell_voltage() + self._dbusservice["/System/MinCellVoltage"] = self.battery.get_min_cell_voltage() + self._dbusservice["/System/MaxCellVoltage"] = self.battery.get_max_cell_voltage() self._dbusservice["/Balancing"] = self.battery.get_balancing() # Update the alarms self._dbusservice["/Alarms/LowVoltage"] = self.battery.protection.voltage_low - self._dbusservice[ - "/Alarms/LowCellVoltage" - ] = self.battery.protection.voltage_cell_low + self._dbusservice["/Alarms/LowCellVoltage"] = self.battery.protection.voltage_cell_low self._dbusservice["/Alarms/HighVoltage"] = self.battery.protection.voltage_high self._dbusservice["/Alarms/LowSoc"] = self.battery.protection.soc_low - self._dbusservice[ - "/Alarms/HighChargeCurrent" - ] = self.battery.protection.current_over - self._dbusservice[ - "/Alarms/HighDischargeCurrent" - ] = self.battery.protection.current_under - self._dbusservice[ - "/Alarms/CellImbalance" - ] = self.battery.protection.cell_imbalance - self._dbusservice[ - "/Alarms/InternalFailure" - ] = self.battery.protection.internal_failure - self._dbusservice[ - "/Alarms/HighChargeTemperature" - ] = self.battery.protection.temp_high_charge - self._dbusservice[ - "/Alarms/LowChargeTemperature" - ] = self.battery.protection.temp_low_charge - self._dbusservice[ - "/Alarms/HighTemperature" - ] = self.battery.protection.temp_high_discharge - self._dbusservice[ - "/Alarms/LowTemperature" - ] = self.battery.protection.temp_low_discharge + self._dbusservice["/Alarms/HighChargeCurrent"] = self.battery.protection.current_over + self._dbusservice["/Alarms/HighDischargeCurrent"] = self.battery.protection.current_under + self._dbusservice["/Alarms/CellImbalance"] = self.battery.protection.cell_imbalance + self._dbusservice["/Alarms/InternalFailure"] = self.battery.protection.internal_failure + self._dbusservice["/Alarms/HighChargeTemperature"] = self.battery.protection.temp_high_charge + self._dbusservice["/Alarms/LowChargeTemperature"] = self.battery.protection.temp_low_charge + self._dbusservice["/Alarms/HighTemperature"] = self.battery.protection.temp_high_discharge + self._dbusservice["/Alarms/LowTemperature"] = self.battery.protection.temp_low_discharge # cell voltages if BATTERY_CELL_DATA_FORMAT > 0: @@ -470,23 +459,42 @@ def publish_dbus(self): try: if ( self.battery.capacity is not None - and len(TIME_TO_SOC_POINTS) > 0 - and self.battery.time_to_soc_update == 0 + and + len(TIME_TO_SOC_POINTS) > 0 + and + ( + ( + # update only once in same second + int(time()) != self.battery.time_to_soc_update + and + # update only every x seconds + int(time()) % TIME_TO_SOC_RECALCULATE_EVERY == 0 + ) + or + # update on first run + self.battery.time_to_soc_update == 0 + ) ): - self.battery.time_to_soc_update = TIME_TO_SOC_LOOP_CYCLES + self.battery.time_to_soc_update = int(time()) crntPrctPerSec = ( abs(self.battery.current / (self.battery.capacity / 100)) / 3600 ) + # Update TimeToGo item, has to be a positive int since it's used from dbus-systemcalc-py + self._dbusservice["/TimeToGo"] = ( + abs ( int ( self.battery.get_timeToSoc(SOC_LOW_WARNING, crntPrctPerSec, True) ) ) + if self.battery.current + else None + ) + + # Update TimeToSoc items for num in TIME_TO_SOC_POINTS: self._dbusservice["/TimeToSoC/" + str(num)] = ( - self.battery.get_timetosoc(num, crntPrctPerSec) + self.battery.get_timeToSoc(num, crntPrctPerSec) if self.battery.current else None ) - else: - self.battery.time_to_soc_update -= 1 except: pass diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh index 1b141d39..28afde26 100644 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disabledriver.sh @@ -1,2 +1,18 @@ -#!/bin/sh -rm -f /data/conf/serial-starter.d \ No newline at end of file +#!/bin/bash +set -x + +DRIVERNAME=dbus-serialbattery + +# handle read only mounts +sh /opt/victronenergy/swupdate-scripts/remount-rw.sh + +# remove files +rm -f /data/conf/serial-starter.d +rm -rf /service/dbus-blebattery-* + +# kill if running +pkill -f "python .*/$DRIVERNAME.py" + +# remove install-script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local diff --git a/etc/dbus-serialbattery/ecs.py b/etc/dbus-serialbattery/ecs.py index 598cc58c..d8bc7295 100644 --- a/etc/dbus-serialbattery/ecs.py +++ b/etc/dbus-serialbattery/ecs.py @@ -7,8 +7,8 @@ class Ecs(Battery): - def __init__(self, port, baud): - super(Ecs, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Ecs, self).__init__(port, baud, address) self.type = self.BATTERYTYPE BATTERYTYPE = "ECS_LiPro" diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 442d86bd..6395c64a 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -1,4 +1,5 @@ -#!/bin/sh +#!/bin/bash +set -x ## DO NOT TOUCH THIS ## install_service() { @@ -6,6 +7,7 @@ install_service() { echo "#!/bin/sh" > /service/dbus-blebattery-$1/log/run echo "exec multilog t s25000 n4 /var/log/dbus-blebattery-$1" >> /service/dbus-blebattery-$1/log/run chmod 755 /service/dbus-blebattery-$1/log/run + echo "#!/bin/sh" > /service/dbus-blebattery-$1/run echo "exec 2>&1" >> /service/dbus-blebattery-$1/run echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery-$1/run diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 1603fc59..cf8e25be 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -x # install required packages opkg update diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index 6b472145..ed8ce289 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -30,42 +30,47 @@ function versionStringToNumber () fi } +# backup old PageBattery.qml once. New firmware upgrade will remove the backup +if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then + cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup +fi +# backup old PageLynxIonIo.qml once. New firmware upgrade will remove the backup +if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then + cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup +fi +# copy new PageBattery.qml +cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ +# copy new PageBatteryCellVoltages +cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ +# copy new PageBatterySetup +cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ +# copy new PageLynxIonIo.qml +cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/ + + # get current Venus OS version versionStringToNumber $(head -n 1 /opt/victronenergy/version) ((venusVersionNumber = $versionNumber)) -# revert to VisualItemModel, if before v3.00~14 (v3.00~14 uses VisibleItemModel) +# revert to VisualItemModel, if Venus OS older than v3.00~14 (v3.00~14 uses VisibleItemModel) versionStringToNumber "v3.00~14" -qmlDir="/data/etc/dbus-serialbattery/qml" +# change in Victron directory, else the files are "broken" if upgrading from v2 to v3 +qmlDir="/opt/victronenergy/gui/qml" if (( $venusVersionNumber < $versionNumber )); then - echo "Venus OS $(head -n 1 /opt/victronenergy/version) is olter than v3.00~14. Replacing VisibleItemModel with VisualItemModel" + echo -n "Venus OS $(head -n 1 /opt/victronenergy/version) is older than v3.00~14. Replacing VisibleItemModel with VisualItemModel... " fileList="$qmlDir/PageBattery.qml" fileList+=" $qmlDir/PageBatteryCellVoltages.qml" fileList+=" $qmlDir/PageBatterySetup.qml" fileList+=" $qmlDir/PageLynxIonIo.qml" for file in $fileList ; do sed -i -e 's/VisibleItemModel/VisualItemModel/' "$file" - done + done + echo "done." fi -# backup old PageBattery.qml once. New firmware upgrade will remove the backup -if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then - cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup -fi -if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then - cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup -fi - -# copy new PageBattery.qml -cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ -# copy new PageLynxIonIo.qml -cp /data/etc/dbus-serialbattery/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/ -# copy new PageBatteryCellVoltages -cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ -cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ # stop gui svc -d /service/gui # sleep 1 sec diff --git a/etc/dbus-serialbattery/installrelease.sh b/etc/dbus-serialbattery/installrelease.sh index 530ef525..2904bbde 100644 --- a/etc/dbus-serialbattery/installrelease.sh +++ b/etc/dbus-serialbattery/installrelease.sh @@ -1,4 +1,5 @@ -#!/bin/sh -curl -s https://api.github.com/repos/Louisvdw/dbus-serialbattery/releases/latest | grep "browser_download_url.*gz" | cut -d : -f 2,3 | tr -d \" | wget -O venus-data.tar.gz -qi - +#!/bin/bash +set -x +curl -s https://api.github.com/repos/Louisvdw/dbus-serialbattery/releases/latest | grep "browser_download_url.*gz" | cut -d : -f 2,3 | tr -d \" | wget -O venus-data.tar.gz -qi - tar -zxf ./venus-data.tar.gz -C /data sh /data/etc/dbus-serialbattery/reinstalllocal.sh diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/jkbms.py index 07baaf8e..655c29af 100644 --- a/etc/dbus-serialbattery/jkbms.py +++ b/etc/dbus-serialbattery/jkbms.py @@ -6,8 +6,8 @@ class Jkbms(Battery): - def __init__(self, port, baud): - super(Jkbms, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Jkbms, self).__init__(port, baud, address) self.type = self.BATTERYTYPE BATTERYTYPE = "Jkbms" @@ -21,14 +21,11 @@ def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. # The result or call should be unique to this BMS. Battery name or version, etc. # Return True if success, False for failure - result = False try: - result = self.read_status_data() + return self.read_status_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") - pass - - return result + return False def get_settings(self): # After successful connection get_settings will be call to set up the battery. @@ -134,6 +131,10 @@ def read_status_data(self): self.to_fet_bits( unpack_from(">H", self.get_data(status_data, b"\x8C", offset, 2))[0] ) + offset = cellbyte_count + 84 + self.to_balance_bits( + unpack_from(">B", self.get_data(status_data, b"\x9D", offset, 1))[0] + ) offset = cellbyte_count + 155 self.production = unpack_from( @@ -144,13 +145,53 @@ def read_status_data(self): ">15s", self.get_data(status_data, b"\xB7", offset, 15) )[0].decode() + # show wich cells are balancing + if self.get_min_cell() is not None and self.get_max_cell() is not None: + for c in range(self.cell_count): + if self.balancing and ( self.get_min_cell() == c or self.get_max_cell() == c ): + self.cells[c].balance = True + else: + self.cells[c].balance = False + # logger.info(self.hardware_version) return True def to_fet_bits(self, byte_data): - tmp = bin(byte_data)[2:].rjust(2, utils.zero_char) - self.charge_fet = is_bit_set(tmp[1]) - self.discharge_fet = is_bit_set(tmp[0]) + tmp = bin(byte_data)[2:].rjust(3, utils.zero_char) + self.charge_fet = is_bit_set(tmp[2]) + self.discharge_fet = is_bit_set(tmp[1]) + self.balancing = is_bit_set(tmp[0]) + + def to_balance_bits(self, byte_data): + tmp = bin(byte_data)[2:] + self.balance_fet = is_bit_set(tmp) + + def get_balancing(self): + return 1 if self.balancing else 0 + + def get_min_cell(self): + min_voltage = 9999 + min_cell = None + for c in range(min(len(self.cells), self.cell_count)): + if ( + self.cells[c].voltage is not None + and min_voltage > self.cells[c].voltage + ): + min_voltage = self.cells[c].voltage + min_cell = c + return min_cell + + def get_max_cell(self): + max_voltage = 0 + max_cell = None + for c in range(min(len(self.cells), self.cell_count)): + if ( + self.cells[c].voltage is not None + and max_voltage < self.cells[c].voltage + ): + max_voltage = self.cells[c].voltage + max_cell = c + return max_cell def to_protection_bits(self, byte_data): pos = 13 @@ -182,8 +223,12 @@ def to_protection_bits(self, byte_data): 1 if is_bit_set(tmp[pos - 4]) or is_bit_set(tmp[pos - 8]) else 0 ) - def read_serial_data_jkbms(self, command): - # use the read_serial_data() function to read the data and then do BMS spesific checks (crc, start bytes, etc) + def read_serial_data_jkbms(self, command: str) -> bool: + """ + use the read_serial_data() function to read the data and then do BMS specific checks (crc, start bytes, etc) + :param command: the command to be sent to the bms + :return: True if everything is fine, else False + """ data = read_serial_data( command, self.port, diff --git a/etc/dbus-serialbattery/lifepower.py b/etc/dbus-serialbattery/lifepower.py index 90e31ce5..cc3058c0 100644 --- a/etc/dbus-serialbattery/lifepower.py +++ b/etc/dbus-serialbattery/lifepower.py @@ -3,14 +3,17 @@ from battery import Protection, Battery, Cell from utils import * from struct import * +import re class Lifepower(Battery): - def __init__(self, port, baud): - super(Lifepower, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Lifepower, self).__init__(port, baud, address) self.type = self.BATTERYTYPE command_general = b"\x7E\x01\x01\x00\xFE\x0D" + command_hardware_version = b"\x7E\x01\x42\x00\xFC\x0D" + command_firmware_version = b"\x7E\x01\x33\x00\xFE\x0D" balancing = 0 BATTERYTYPE = "EG4 Lifepower" LENGTH_CHECK = 5 @@ -27,10 +30,29 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT + self.max_battery_current = MAX_BATTERY_CURRENT self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT - self.version = "EG4 BMS V1.0" - logger.info(self.hardware_version) + hardware_version = self.read_serial_data_eg4(self.command_hardware_version) + if hardware_version: + # I get some characters that I'm not able to figure out the encoding, probably chinese so I discard it + # Also remove any special character that is not printable or make no sense. + self.hardware_version = re.sub( + r"[^a-zA-Z0-9-._ ]", + "", + str(hardware_version, encoding="utf-8", errors="ignore"), + ) + logger.info("Hardware Version:" + self.hardware_version) + + version = self.read_serial_data_eg4(self.command_firmware_version) + if version: + self.version = re.sub( + r"[^a-zA-Z0-9-._ ]", "", str(version, encoding="utf-8", errors="ignore") + ) + logger.info("Firmware Version:" + self.version) + + # polling every second seems to create some error messages + # change to 2 seconds + self.poll_interval = 2000 return True def refresh_data(self): @@ -63,7 +85,6 @@ def read_status_data(self): for i in range(0, len(group_payload), 2) ] ) - i = end # Cells @@ -73,7 +94,10 @@ def read_status_data(self): self.cells = [Cell(True) for _ in range(0, self.cell_count)] for i, cell in enumerate(self.cells): - cell.voltage = groups[0][i] / 1000 + # there is a situation where the MSB bit of the high byte may come set + # I got that when I got a high voltage alarm from the unit. + # make sure that bit is 0, by doing an AND with 32767 (01111111 1111111) + cell.voltage = (groups[0][i] & 32767) / 1000 # Current self.current = (30000 - groups[1][0]) / 100 @@ -85,9 +109,13 @@ def read_status_data(self): self.capacity = groups[3][0] / 100 # Temperature - # TODO There is a significant amount of temperature information being ignored here - # see https://github.com/slim-bean/powermon#group-5 - self.temp1 = groups[4][0] - 50 + self.temp_sensors = 6 + self.temp1 = (groups[4][0] & 0xFF) - 50 + self.temp2 = (groups[4][1] & 0xFF) - 50 + self.temp3 = (groups[4][2] & 0xFF) - 50 + self.temp4 = (groups[4][3] & 0xFF) - 50 + self.temp5 = (groups[4][4] & 0xFF) - 50 + self.temp6 = (groups[4][5] & 0xFF) - 50 # Alarms # 4th bit: Over Current Protection @@ -106,11 +134,6 @@ def read_status_data(self): # Voltage self.voltage = groups[7][0] / 100 - - # TODO State of health - - self.hardware_version = "EG4 Lifepower " + str(self.cell_count) + " cells" - return True def get_balancing(self): diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index e8beb480..9af90c02 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -45,8 +45,8 @@ def set_software_lock(self, value): class LltJbd(Battery): - def __init__(self, port, baud): - super(LltJbd, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(LltJbd, self).__init__(port, baud, address) self.protection = LltJbdProtection() self.type = self.BATTERYTYPE diff --git a/etc/dbus-serialbattery/mnb.py b/etc/dbus-serialbattery/mnb.py index 233d624a..8e91bd5b 100644 --- a/etc/dbus-serialbattery/mnb.py +++ b/etc/dbus-serialbattery/mnb.py @@ -48,7 +48,7 @@ def set_software_lock(self, value): class MNB(Battery): def __init__(self, port, baud, address=0): - super(MNB, self).__init__(port, baud) + super(MNB, self).__init__(port, baud, address) self.protection = MNBProtection() self.hardware_version = 1.02 self.voltage = 26 diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 14eade66..b553ba6a 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -1,11 +1,12 @@ -#!/bin/sh +#!/bin/bash +set -x DRIVERNAME=dbus-serialbattery -#handle read only mounts +# handle read only mounts sh /opt/victronenergy/swupdate-scripts/remount-rw.sh -#install +# install rm -rf /opt/victronenergy/service/$DRIVERNAME rm -rf /opt/victronenergy/service-templates/$DRIVERNAME rm -rf /opt/victronenergy/$DRIVERNAME @@ -14,7 +15,15 @@ cp -f /data/etc/$DRIVERNAME/* /opt/victronenergy/$DRIVERNAME &>/dev/null cp -rf /data/etc/$DRIVERNAME/service /opt/victronenergy/service-templates/$DRIVERNAME sh /data/etc/$DRIVERNAME/installqml.sh -#restart if running +# check if serial-starter.d was deleted +serialstarter=/data/conf/serial-starter.d +if [ ! -f $serialstarter ]; then + echo "service sbattery dbus-serialbattery" >> $serialstarter + echo "alias default gps:vedirect:sbattery" >> $serialstarter + echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" >> $serialstarter +fi + +# restart if running pkill -f "python .*/$DRIVERNAME.py" # add install-script to rc.local to be ready for firmware update diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/renogy.py index 66ad7aa7..6f1c6c6f 100644 --- a/etc/dbus-serialbattery/renogy.py +++ b/etc/dbus-serialbattery/renogy.py @@ -6,7 +6,7 @@ class Renogy(Battery): def __init__(self, port, baud, address): - super(Renogy, self).__init__(port, baud) + super(Renogy, self).__init__(port, baud, address) self.type = self.BATTERYTYPE # The RBT100LFP12SH-G1 uses 0xF7, another battery uses 0x30 diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restoregui.sh index bd521103..f26bf9cc 100644 --- a/etc/dbus-serialbattery/restoregui.sh +++ b/etc/dbus-serialbattery/restoregui.sh @@ -1,5 +1,5 @@ -#!/bin/sh - +#!/bin/bash +set -x #restore original backup cp -f /opt/victronenergy/gui/qml/PageBattery.qml.backup /opt/victronenergy/gui/qml/PageBattery.qml diff --git a/etc/dbus-serialbattery/revov.py b/etc/dbus-serialbattery/revov.py index 40b245a1..d3fc2563 100755 --- a/etc/dbus-serialbattery/revov.py +++ b/etc/dbus-serialbattery/revov.py @@ -18,8 +18,8 @@ class Revov(Battery): - def __init__(self, port, baud): - super(Revov, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Revov, self).__init__(port, baud, address) self.type = self.BATTERYTYPE self.soc = 100 self.voltage = None diff --git a/etc/dbus-serialbattery/sinowealth.py b/etc/dbus-serialbattery/sinowealth.py index 8788c60e..f5021ff5 100755 --- a/etc/dbus-serialbattery/sinowealth.py +++ b/etc/dbus-serialbattery/sinowealth.py @@ -5,8 +5,8 @@ class Sinowealth(Battery): - def __init__(self, port, baud): - super(Sinowealth, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(Sinowealth, self).__init__(port, baud, address) self.poll_interval = 2000 self.type = self.BATTERYTYPE diff --git a/etc/dbus-serialbattery/start-serialbattery.sh b/etc/dbus-serialbattery/start-serialbattery.sh index c6f79742..38de1d58 100644 --- a/etc/dbus-serialbattery/start-serialbattery.sh +++ b/etc/dbus-serialbattery/start-serialbattery.sh @@ -1,5 +1,5 @@ #!/bin/bash -# +set -x . /opt/victronenergy/serial-starter/run-service.sh diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh new file mode 100644 index 00000000..3d44bcec --- /dev/null +++ b/etc/dbus-serialbattery/uninstall.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -x + +DRIVERNAME=dbus-serialbattery + +# handle read only mounts +sh /opt/victronenergy/swupdate-scripts/remount-rw.sh + +# remove files +rm -f /data/conf/serial-starter.d +rm -rf /opt/victronenergy/service/$DRIVERNAME +rm -rf /opt/victronenergy/service-templates/$DRIVERNAME +rm -rf /opt/victronenergy/$DRIVERNAME +rm -rf /service/dbus-blebattery-* + +# kill if running +pkill -f "python .*/$DRIVERNAME.py" + +# remove install-script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local + +# remove cronjob +sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 28e644b3..b56904c0 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- import logging + +import configparser +from pathlib import Path +from typing import List, Any, Callable + import serial from time import sleep from struct import unpack_from @@ -10,163 +15,250 @@ logger = logging.getLogger("SerialBattery") logger.setLevel(logging.INFO) +config = configparser.ConfigParser() +path = Path(__file__).parents[0] +default_config_file_path = path.joinpath("config.default.ini").absolute().__str__() +custom_config_file_path = path.joinpath("config.ini").absolute().__str__() +config.read([default_config_file_path, custom_config_file_path]) + + +def _get_list_from_config( + group: str, option: str, mapper: Callable[[Any], Any] = lambda v: v +) -> List[Any]: + rawList = config[group][option].split(",") + return list( + map(mapper, [item for item in rawList if item != "" and item is not None]) + ) + + # battery types # if not specified: baud = 9600 -battery_types = [ - {"bms": "LltJbd"}, - {"bms": "Ant", "baud": 19200}, - {"bms": "Daly", "address": b"\x40"}, - {"bms": "Daly", "address": b"\x80"}, - {"bms": "Jkbms", "baud": 115200}, - # {"bms" : "Sinowealth"}, - {"bms": "Lifepower"}, - {"bms": "Renogy", "address": b"\x30"}, - {"bms": "Renogy", "address": b"\xF7"}, - {"bms": "Ecs", "baud": 19200}, - # {"bms" : "MNB"}, -] # Constants - Need to dynamically get them in future -DRIVER_VERSION = '0.15' -DRIVER_SUBVERSION = ".0-ble" +DRIVER_VERSION = "1.0" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230413)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" # Choose the mode for voltage / current limitations (True / False) -# False is a Step mode. This is the default with limitations on hard boundary steps -# True "Linear" # New linear limitations by WaldemarFech for smoother values -LINEAR_LIMITATION_ENABLE = False - -# -------- Cell Voltage limitation --------- -# Description: -# Maximal charge / discharge current will be in-/decreased depending on min- and max-cell-voltages -# Example: 18cells * 3.55V/cell = 63.9V max charge voltage. 18 * 2.7V = 48,6V min discharge voltage -# ... but the (dis)charge current will be (in-/)decreased, if even ONE SINGLE BATTERY CELL reaches the limits +# False is a step mode. This is the default with limitations on hard boundary steps +# True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) +# For CVL the penalties are only applied, if the cell voltage reaches the penalty voltage +LINEAR_LIMITATION_ENABLE = "True" == config["DEFAULT"]["LINEAR_LIMITATION_ENABLE"] + +# Battery Current limits +MAX_BATTERY_CHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_CHARGE_CURRENT"]) +MAX_BATTERY_DISCHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"]) + + +# --------- Charge Voltage limitation (affecting CVL) --------- +# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) +# after max voltage is reached for MAX_VOLTAGE_TIME_SEC. It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT +# If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied +# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. +# Allow max voltage of 55.2V again, if SoC is once below 90% +# Charge voltage control management enable (True/False). +CVCM_ENABLE = "True" == config["DEFAULT"]["CVCM_ENABLE"] + +# -- Cell Voltages +# Description: Cell min/max voltages which are used to calculate the min/max battery voltage +# Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage +MIN_CELL_VOLTAGE = float(config["DEFAULT"]["MIN_CELL_VOLTAGE"]) +MAX_CELL_VOLTAGE = float(config["DEFAULT"]["MAX_CELL_VOLTAGE"]) +# Max voltage can seen as absorption voltage +FLOAT_CELL_VOLTAGE = float(config["DEFAULT"]["FLOAT_CELL_VOLTAGE"]) + +# -- Penalty Voltages +# NOTE: works only when LINEAR_LIMITATION_ENABLE = True +# More details can be found here: https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635 +# If the cell voltage reaches 3.48V, then reduce actual battery-voltage by 0.01V +# If the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded +# There will be a sum of all penalties for each cell, which exceeds the limits +# NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of PENALTY_BATTERY_VOLTAGE, +# else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. +PENALTY_AT_CELL_VOLTAGE = _get_list_from_config( + "DEFAULT", + "PENALTY_AT_CELL_VOLTAGE", + lambda v: float(v) +) +# this voltage will be subtracted +PENALTY_BATTERY_VOLTAGE = _get_list_from_config( + "DEFAULT", + "PENALTY_BATTERY_VOLTAGE", + lambda v: float(v) +) +# Specify in seconds how often the penalty should be recalculated +PENALTY_RECALCULATE_EVERY = int(config["DEFAULT"]["PENALTY_RECALCULATE_EVERY"]) + +# -- CVL Reset based on SoC option +# Reset max voltage after +MAX_VOLTAGE_TIME_SEC = float(config["DEFAULT"]["MAX_VOLTAGE_TIME_SEC"]) +# Specify SoC where CVL limit is reset to max voltage +SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = float(config["DEFAULT"]["SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT"]) + + +# --------- Cell Voltage Current limitation (affecting CCL/DCL) --------- +# Description: Maximal charge / discharge current will be in-/decreased depending on min and max cell voltages +# Example: 18 cells * 3.55V/cell = 63.9V max charge voltage +# 18 cells * 2.70V/cell = 48.6V min discharge voltage +# But in reality not all cells reach the same voltage at the same time. The (dis)charge current +# will be (in-/)decreased, if even ONE SINGLE BATTERY CELL reaches the limits # Charge current control management referring to cell-voltage enable (True/False). -CCCM_CV_ENABLE = True +CCCM_CV_ENABLE = "True" == config["DEFAULT"]["CCCM_CV_ENABLE"] # Discharge current control management referring to cell-voltage enable (True/False). -DCCM_CV_ENABLE = True - -# Set Steps to reduce battery current. The current will be changed linear between those steps -CELL_VOLTAGES_WHILE_CHARGING = [3.55, 3.50, 3.45, 3.30] -MAX_CHARGE_CURRENT_CV = [0, 2, 30, 60] - -CELL_VOLTAGES_WHILE_DISCHARGING = [2.70, 2.80, 2.90, 3.10] -MAX_DISCHARGE_CURRENT_CV = [0, 5, 30, 60] - -# -------- Temperature limitation --------- -# Description: -# Maximal charge / discharge current will be in-/decreased depending on temperature +DCCM_CV_ENABLE = "True" == config["DEFAULT"]["DCCM_CV_ENABLE"] + +# Set steps to reduce battery current +# The current will be changed linear between those steps if LINEAR_LIMITATION_ENABLE is set to True +CELL_VOLTAGES_WHILE_CHARGING = _get_list_from_config( + "DEFAULT", "CELL_VOLTAGES_WHILE_CHARGING", lambda v: float(v) +) +MAX_CHARGE_CURRENT_CV = _get_list_from_config( + "DEFAULT", + "MAX_CHARGE_CURRENT_CV_FRACTION", + lambda v: MAX_BATTERY_CHARGE_CURRENT * float(v), +) + +CELL_VOLTAGES_WHILE_DISCHARGING = _get_list_from_config( + "DEFAULT", "CELL_VOLTAGES_WHILE_DISCHARGING", lambda v: float(v) +) +MAX_DISCHARGE_CURRENT_CV = _get_list_from_config( + "DEFAULT", + "MAX_DISCHARGE_CURRENT_CV_FRACTION", + lambda v: MAX_BATTERY_DISCHARGE_CURRENT * float(v), +) + + +# --------- Temperature limitation (affecting CCL/DCL) --------- +# Description: Maximal charge / discharge current will be in-/decreased depending on temperature # Example: The temperature limit will be monitored to control the currents. If there are two temperature senors, # then the worst case will be calculated and the more secure lower current will be set. # Charge current control management referring to temperature enable (True/False). -CCCM_T_ENABLE = True +CCCM_T_ENABLE = "True" == config["DEFAULT"]["CCCM_T_ENABLE"] # Charge current control management referring to temperature enable (True/False). -DCCM_T_ENABLE = True - -# Set Steps to reduce battery current. The current will be changed linear between those steps -TEMPERATURE_LIMITS_WHILE_CHARGING = [55, 40, 35, 5, 2, 0] -MAX_CHARGE_CURRENT_T = [0, 28, 60, 60, 28, 0] - -TEMPERATURE_LIMITS_WHILE_DISCHARGING = [55, 40, 35, 5, 0, -20] -MAX_DISCHARGE_CURRENT_T = [0, 28, 60, 60, 28, 0] - -# if the cell voltage reaches 3.55V, then reduce current battery-voltage by 0.01V -# if the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded -# there will be a sum of all penalties for each cell, which exceeds the limits -PENALTY_AT_CELL_VOLTAGE = [3.45, 3.55, 3.6] -PENALTY_BATTERY_VOLTAGE = [0.01, 1.0, 2.0] # this voltage will be subtracted - - -# -------- SOC limitation --------- -# Description: -# Maximal charge / discharge current will be increased / decreased depending on State of Charge, see CC_SOC_LIMIT1 etc. -# The State of Charge (SoC) charge / discharge current will be in-/decreased depending on SOC. -# Example: 16cells * 3.45V/cell = 55,2V max charge voltage. 16*2.9V = 46,4V min discharge voltage -# Cell min/max voltages - used with the cell count to get the min/max battery voltage -MIN_CELL_VOLTAGE = 2.9 -MAX_CELL_VOLTAGE = 3.45 -FLOAT_CELL_VOLTAGE = 3.35 -MAX_VOLTAGE_TIME_SEC = 15 * 60 -SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90 - -# battery Current limits -MAX_BATTERY_CHARGE_CURRENT = 50.0 -MAX_BATTERY_DISCHARGE_CURRENT = 60.0 - +DCCM_T_ENABLE = "True" == config["DEFAULT"]["DCCM_T_ENABLE"] + +# Set steps to reduce battery current +# The current will be changed linear between those steps if LINEAR_LIMITATION_ENABLE is set to True +TEMPERATURE_LIMITS_WHILE_CHARGING = _get_list_from_config( + "DEFAULT", + "TEMPERATURE_LIMITS_WHILE_CHARGING", + lambda v: float(v) +) +MAX_CHARGE_CURRENT_T = _get_list_from_config( + "DEFAULT", + "MAX_CHARGE_CURRENT_T_FRACTION", + lambda v: MAX_BATTERY_CHARGE_CURRENT * float(v), +) + +TEMPERATURE_LIMITS_WHILE_DISCHARGING = _get_list_from_config( + "DEFAULT", "TEMPERATURE_LIMITS_WHILE_DISCHARGING", lambda v: float(v) +) +MAX_DISCHARGE_CURRENT_T = _get_list_from_config( + "DEFAULT", + "MAX_DISCHARGE_CURRENT_T_FRACTION", + lambda v: MAX_BATTERY_DISCHARGE_CURRENT * float(v), +) + + +# --------- SOC limitation (affecting CCL/DCL) --------- +# Description: Maximal charge / discharge current will be increased / decreased depending on State of Charge, +# see CC_SOC_LIMIT1 etc. +# Example: The SoC limit will be monitored to control the currents. # Charge current control management enable (True/False). -CCCM_SOC_ENABLE = True +CCCM_SOC_ENABLE = "True" == config["DEFAULT"]["CCCM_SOC_ENABLE"] # Discharge current control management enable (True/False). -DCCM_SOC_ENABLE = True - -# charge current soc limits -CC_SOC_LIMIT1 = 98 -CC_SOC_LIMIT2 = 95 -CC_SOC_LIMIT3 = 91 - -# charge current limits -CC_CURRENT_LIMIT1 = 5 -CC_CURRENT_LIMIT2 = MAX_BATTERY_CHARGE_CURRENT / 4 -CC_CURRENT_LIMIT3 = MAX_BATTERY_CHARGE_CURRENT / 2 - -# discharge current soc limits -DC_SOC_LIMIT1 = 10 -DC_SOC_LIMIT2 = 20 -DC_SOC_LIMIT3 = 30 +DCCM_SOC_ENABLE = "True" == config["DEFAULT"]["DCCM_SOC_ENABLE"] + +# Charge current soc limits +CC_SOC_LIMIT1 = float(config["DEFAULT"]["CC_SOC_LIMIT1"]) +CC_SOC_LIMIT2 = float(config["DEFAULT"]["CC_SOC_LIMIT2"]) +CC_SOC_LIMIT3 = float(config["DEFAULT"]["CC_SOC_LIMIT3"]) + +# Charge current limits +CC_CURRENT_LIMIT1 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT1_FRACTION"]) +CC_CURRENT_LIMIT2 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT2_FRACTION"]) +CC_CURRENT_LIMIT3 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT3_FRACTION"]) + +# Discharge current soc limits +DC_SOC_LIMIT1 = float(config["DEFAULT"]["DC_SOC_LIMIT1"]) +DC_SOC_LIMIT2 = float(config["DEFAULT"]["DC_SOC_LIMIT2"]) +DC_SOC_LIMIT3 = float(config["DEFAULT"]["DC_SOC_LIMIT3"]) + +# Discharge current limits +DC_CURRENT_LIMIT1 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT1_FRACTION"]) +DC_CURRENT_LIMIT2 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT2_FRACTION"]) +DC_CURRENT_LIMIT3 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT3_FRACTION"]) + + +# --------- Time-To-Soc --------- +# Description: Calculates the time to a specific SoC +# Example: TIME_TO_SOC_POINTS = 50, 25, 15, 0 +# 6h 24m remaining until 50% SoC +# 17h 36m remaining until 25% SoC +# 22h 5m remaining until 15% SoC +# 28h 48m remaining until 0% SoC +# Set of SoC percentages to report on dbus and MQTT. The more you specify the more it will impact system performance. +# [Valid values 0-100, comma separated list. More that 20 intervals are not recommended] +# Example: TIME_TO_SOC_POINTS = 100, 95, 90, 85, 75, 50, 25, 20, 10, 0 +# Leave empty to disable +TIME_TO_SOC_POINTS = _get_list_from_config("DEFAULT", "TIME_TO_SOC_POINTS", lambda v: int(v)) +# Specify TimeToSoc value type [Valid values 1, 2, 3] +# 1 Seconds +# 2 Time string d h m s +# 3 Both seconds and time string " [d h m s]" +TIME_TO_SOC_VALUE_TYPE = int(config["DEFAULT"]["TIME_TO_SOC_VALUE_TYPE"]) +# Specify in seconds how often the TimeToSoc should be recalculated +# Minimum are 5 seconds to prevent CPU overload +TIME_TO_SOC_RECALCULATE_EVERY = int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) if int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) > 5 else 5 +# Include TimeToSoC points when moving away from the SoC point [Valid values True, False] +# These will be as negative time. Disabling this improves performance slightly +TIME_TO_SOC_INC_FROM = "True" == config["DEFAULT"]["TIME_TO_SOC_INC_FROM"] + + +# --------- Additional settings --------- +# Specify only one BMS type to load else leave empty to try to load all availabe +# LltJbd, Ant, Daly, Daly, Jkbms, Lifepower, Renogy, Renogy, Ecs +BMS_TYPE = config["DEFAULT"]["BMS_TYPE"] + +# Publish the config settings to the dbus path "/Info/Config/" +PUBLISH_CONFIG_VALUES = int(config["DEFAULT"]["PUBLISH_CONFIG_VALUES"]) + +# Select the format of cell data presented on dbus [Valid values 0,1,2,3] +# 0 Do not publish all the cells (only the min/max cell data as used by the default GX) +# 1 Format: /Voltages/Cell (also available for display on Remote Console) +# 2 Format: /Cell/#/Volts +# 3 Both formats 1 and 2 +BATTERY_CELL_DATA_FORMAT = int(config["DEFAULT"]["BATTERY_CELL_DATA_FORMAT"]) -# discharge current limits -DC_CURRENT_LIMIT1 = 5 -DC_CURRENT_LIMIT2 = MAX_BATTERY_DISCHARGE_CURRENT / 4 -DC_CURRENT_LIMIT3 = MAX_BATTERY_DISCHARGE_CURRENT / 2 +# Simulate Midpoint graph (True/False). +MIDPOINT_ENABLE = "True" == config["DEFAULT"]["MIDPOINT_ENABLE"] -# Charge voltage control management enable (True/False). -CVCM_ENABLE = False -# Simulate Midpoint graph (True/False). -MIDPOINT_ENABLE = False +# --------- BMS specific settings --------- -# soc low levels -SOC_LOW_WARNING = 20 -SOC_LOW_ALARM = 10 +# -- LltJbd settings +# SoC low levels +# NOTE: SOC_LOW_WARNING is also used to calculate the Time-To-Go even if you are not using a LltJbd BMS +SOC_LOW_WARNING = float(config["DEFAULT"]["SOC_LOW_WARNING"]) +SOC_LOW_ALARM = float(config["DEFAULT"]["SOC_LOW_ALARM"]) -# Daly settings +# -- Daly settings # Battery capacity (amps) if the BMS does not support reading it -BATTERY_CAPACITY = 50 +BATTERY_CAPACITY = float(config["DEFAULT"]["BATTERY_CAPACITY"]) # Invert Battery Current. Default non-inverted. Set to -1 to invert -INVERT_CURRENT_MEASUREMENT = 1 - -# TIME TO SOC settings [Valid values 0-100, but I don't recommend more that 20 intervals] -# Set of SoC percentages to report on dbus. The more you specify the more it will impact system performance. -# TIME_TO_SOC_POINTS = [100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0] -# Every 5% SoC -# TIME_TO_SOC_POINTS = [100, 95, 90, 85, 75, 50, 25, 20, 10, 0] -TIME_TO_SOC_POINTS = [] # No data set to disable -# Specify TimeToSoc value type: [Valid values 1,2,3] -# TIME_TO_SOC_VALUE_TYPE = 1 # Seconds -# TIME_TO_SOC_VALUE_TYPE = 2 # Time string HH:MN:SC -TIME_TO_SOC_VALUE_TYPE = 3 # Both Seconds and time str " [days, HR:MN:SC]" -# Specify how many loop cycles between each TimeToSoc updates -TIME_TO_SOC_LOOP_CYCLES = 5 -# Include TimeToSoC points when moving away from the SoC point. [Valid values True,False] -# These will be as negative time. Disabling this improves performance slightly. -TIME_TO_SOC_INC_FROM = False - - -# Select the format of cell data presented on dbus. [Valid values 0,1,2,3] -# 0 Do not publish all the cells (only the min/max cell data as used by the default GX) -# 1 Format: /Voltages/Cell# (also available for display on Remote Console) -# 2 Format: /Cell/#/Volts -# 3 Both formats 1 and 2 -BATTERY_CELL_DATA_FORMAT = 1 +INVERT_CURRENT_MEASUREMENT = int(config["DEFAULT"]["INVERT_CURRENT_MEASUREMENT"]) -# Settings for ESC GreenMeter and Lipro devices -GREENMETER_ADDRESS = 1 -LIPRO_START_ADDRESS = 2 -LIPRO_END_ADDRESS = 4 -LIPRO_CELL_COUNT = 15 +# -- ESC GreenMeter and Lipro device settings +GREENMETER_ADDRESS = int(config["DEFAULT"]["GREENMETER_ADDRESS"]) +LIPRO_START_ADDRESS = int(config["DEFAULT"]["LIPRO_START_ADDRESS"]) +LIPRO_END_ADDRESS = int(config["DEFAULT"]["LIPRO_END_ADDRESS"]) +LIPRO_CELL_COUNT = int(config["DEFAULT"]["LIPRO_CELL_COUNT"]) + +# --------- Functions --------- def constrain(val, min_val, max_val): if min_val > max_val: min_val, max_val = max_val, min_val @@ -267,7 +359,12 @@ def open_serial_port(port, baud): # Read data from previously openned serial port def read_serialport_data( - ser, command, length_pos, length_check, length_fixed=None, length_size=None + ser: serial.Serial, + command, + length_pos, + length_check, + length_fixed=None, + length_size=None, ): try: ser.flushOutput() @@ -330,3 +427,19 @@ def read_serialport_data( except serial.SerialException as e: logger.error(e) return False + + +locals_copy = locals().copy() + + +def publish_config_variables(dbusservice): + for variable, value in locals_copy.items(): + if variable.startswith("__"): + continue + if ( + isinstance(value, float) + or isinstance(value, int) + or isinstance(value, str) + or isinstance(value, List) + ): + dbusservice.add_path(f"/Info/Config/{variable}", value) diff --git a/requirements.txt b/requirements.txt index b8b16d03..8bec2184 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ pyserial==3.5 -velib_python==2.80 minimalmodbus==2.0.1 From 534c483475c8daf8a589ea81192e55976acc4881 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 13 Apr 2023 11:42:23 +0200 Subject: [PATCH 054/209] fixes --- etc/dbus-serialbattery/dbus-serialbattery.py | 1 + etc/dbus-serialbattery/jkbms_ble.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 59c0bb5c..a03293e6 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -23,6 +23,7 @@ from daly import Daly from ant import Ant from jkbms import Jkbms +from jkbms_ble import Jkbms_Ble # from sinowealth import Sinowealth from renogy import Renogy diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index b94945d8..369ef848 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -13,7 +13,7 @@ class Jkbms_Ble(Battery): resetting = False def __init__(self, port, baud, address): - super(Jkbms_Ble, self).__init__(address.replace(":", "").lower(), baud) + super(Jkbms_Ble, self).__init__(address.replace(":", "").lower(), baud, address) self.type = self.BATTERYTYPE self.jk = JkBmsBle(address) @@ -43,10 +43,10 @@ def test_connection(self): logger.error("no BMS found at " + self.jk.address) return False - + """ # before indipended service, has to be checked - + logger.info("test of jkbmsble") tries = 0 while True: @@ -165,7 +165,7 @@ def refresh_data(self): self.balancing = False if st["cell_info"]["balancing_action"] == 0.000 else True self.balancing_current = ( st["cell_info"]["balancing_current"] - if st["cell_info"]["balancing_current"] < 32768 + if st["cell_info"]["balancing_current"] < 32768 else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 ) self.balancing_action = st["cell_info"]["balancing_action"] @@ -174,7 +174,7 @@ def refresh_data(self): for c in range(self.cell_count): if self.balancing and ( st["cell_info"]["min_voltage_cell"] == c - or st["cell_info"]["max_voltage_cell"] == c + or st["cell_info"]["max_voltage_cell"] == c ): self.cells[c].balance = True else: From 9c0290f3cb1283d38c6f8f28ec57d03bee268801 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 11:54:15 +0200 Subject: [PATCH 055/209] moved ble part to installble.sh --- etc/dbus-serialbattery/installble.sh | 19 ++++++++++++++++--- etc/dbus-serialbattery/installlocal.sh | 18 ++++++++---------- etc/dbus-serialbattery/reinstalllocal.sh | 1 - 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 6395c64a..aa93006a 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -1,7 +1,18 @@ #!/bin/bash set -x -## DO NOT TOUCH THIS ## +# install required packages +# TO DO: Check first if packages are already installed +opkg update +opkg install python3-misc python3-pip +pip3 install bleak + +# setup cronjob to restart Bluetooth +grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root + +# add install-script to rc.local to be ready for firmware update +grep -qxF "sh /data/etc/$DRIVERNAME/installble.sh" $filename || echo "sh /data/etc/$DRIVERNAME/installble.sh" >> $filename + install_service() { mkdir -p /service/dbus-blebattery-$1/log echo "#!/bin/sh" > /service/dbus-blebattery-$1/log/run @@ -14,9 +25,11 @@ install_service() { echo "python /data/etc/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery-$1/run chmod 755 /service/dbus-blebattery-$1/run } -## END DO NOT TOUCH AREA ## -## Uncomment for each adapter here, increase the number for each service + +## CONFIG AREA + +## Uncomment for each adapter here, increase the number for each adapter/service # install_service 0 Jkbms_Ble C8:47:8C:12:34:56 # install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index cf8e25be..d1c32f8d 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,20 +1,18 @@ #!/bin/sh set -x -# install required packages -opkg update -opkg install python3-misc python3-pip -pip3 install bleak - # install driver tar -zxf ./venus-data.tar.gz -C /data sh /data/etc/dbus-serialbattery/reinstalllocal.sh -# setup cronjob to restart Bluetooth -grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root - -echo "Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." +echo +echo "BLUETOOTH battery connection: There are a few more steps to complete installation." +echo "1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo "2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." +echo "3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create" echo "ATTENTION!" -echo "- Put your Bluetooth MAC adress in /data/etc/dbus-serialbattery/installble.sh and make sure to uncomment at least one install_service line at the bottom of the file." echo "- If you changed the default connection PIN of JKBMS, then you have to pair the JKBMS first using OS tools like the \"bluetoothctl\"." echo "See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." +echo +echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"config.default.ini\" and add them to \"config.ini\" to persist future driver updates." diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index b553ba6a..48bd19e2 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -33,4 +33,3 @@ if [ ! -f $filename ]; then chmod 755 $filename fi grep -qxF "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" $filename || echo "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" >> $filename -grep -qxF "sh /data/etc/$DRIVERNAME/installble.sh" $filename || echo "sh /data/etc/$DRIVERNAME/installble.sh" >> $filename From ee840ce24769633e742f944a917dc4c6213f2637 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 11:54:47 +0200 Subject: [PATCH 056/209] added script to install directly from repository --- etc/dbus-serialbattery/install-nightly.sh | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 etc/dbus-serialbattery/install-nightly.sh diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh new file mode 100644 index 00000000..d39d4265 --- /dev/null +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +PS3="Select the branch you want to install the unreleased code (possible bugs included): " + +select branch in master jkbms_ble quit +do + case $branch in + master) + echo "Selected branch: $branch" + #echo "Selected number: $REPLY" + break + ;; + jkbms_ble) + echo "Selected branch: $branch" + #echo "Selected number: $REPLY" + break + ;; + quit) + exit 0 + ;; + *) + echo "Invalid option $REPLY" + ;; + esac +done + + +cd /tmp + +wget https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip +unzip $branch.zip + +cp /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc + +chmod +x /data/etc/dbus-serialbattery/*.sh +chmod +x /data/etc/dbus-serialbattery/*.py +chmod +x /data/etc/dbus-serialbattery/service/run +chmod +x /data/etc/dbus-serialbattery/service/log/run + +bash /data/etc/dbus-serialbattery/install-local.sh + +if [[ $branch == "jkbms_ble" ]]; then + nano /data/etc/dbus-serialbattery/install-ble.sh +fi From 9488b8385478110a3aa21b243d40dffddaeeffe3 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 11:58:32 +0200 Subject: [PATCH 057/209] added QML --- etc/dbus-serialbattery/restoregui.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restoregui.sh index f26bf9cc..2dae5336 100644 --- a/etc/dbus-serialbattery/restoregui.sh +++ b/etc/dbus-serialbattery/restoregui.sh @@ -1,7 +1,15 @@ #!/bin/bash set -x -#restore original backup -cp -f /opt/victronenergy/gui/qml/PageBattery.qml.backup /opt/victronenergy/gui/qml/PageBattery.qml +#restore original backup +if [ -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then + cp -f /opt/victronenergy/gui/qml/PageBattery.qml.backup /opt/victronenergy/gui/qml/PageBattery.qml + echo "PageBattery.qml was restored." +fi +# backup old PageLynxIonIo.qml once. New firmware upgrade will remove the backup +if [ -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then + cp -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup /opt/victronenergy/gui/qml/PageLynxIonIo.qml + echo "PageLynxIonIo.qml was restored." +fi #stop gui svc -d /service/gui From 953fbb4c1a2864f7216de1976d8b41cadea95c86 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 11:58:55 +0200 Subject: [PATCH 058/209] create empty config.ini for easier user usage --- etc/dbus-serialbattery/reinstalllocal.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 48bd19e2..00f96c3a 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -33,3 +33,12 @@ if [ ! -f $filename ]; then chmod 755 $filename fi grep -qxF "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" $filename || echo "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" >> $filename + +# add empty config.ini, if it does not exist to make it easier for users to add custom settings +filename=/data/etc/$DRIVERNAME/config.ini +if [ ! -f $filename ]; then + echo "[DEFAULT]" > $filename + echo "" >> $filename + echo "; If you want to add custom settings, then check the settings you want to change in \"config.default.ini\"" >> $filename + echo "; and add them below to persist future driver updates." >> $filename +fi From 92bb2a0116d5750b36c885a4fafc73ba6590c2e8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 12:04:12 +0200 Subject: [PATCH 059/209] optimize installation scripts --- etc/dbus-serialbattery/installlocal.sh | 4 +++- etc/dbus-serialbattery/installrelease.sh | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index d1c32f8d..7f5e4acf 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,8 +1,10 @@ #!/bin/sh set -x -# install driver +# extract driver tar -zxf ./venus-data.tar.gz -C /data + +# install driver sh /data/etc/dbus-serialbattery/reinstalllocal.sh echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." diff --git a/etc/dbus-serialbattery/installrelease.sh b/etc/dbus-serialbattery/installrelease.sh index 2904bbde..5bb32ce9 100644 --- a/etc/dbus-serialbattery/installrelease.sh +++ b/etc/dbus-serialbattery/installrelease.sh @@ -1,5 +1,8 @@ #!/bin/bash set -x + +# download latest release curl -s https://api.github.com/repos/Louisvdw/dbus-serialbattery/releases/latest | grep "browser_download_url.*gz" | cut -d : -f 2,3 | tr -d \" | wget -O venus-data.tar.gz -qi - -tar -zxf ./venus-data.tar.gz -C /data -sh /data/etc/dbus-serialbattery/reinstalllocal.sh + +# extract and install driver +sh /data/etc/dbus-serialbattery/installlocal.sh From cd8a30fe57867a1df21fc7c28d2c9762210ec4d6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 12:09:45 +0200 Subject: [PATCH 060/209] changed bash output --- etc/dbus-serialbattery/disabledriver.sh | 4 +++- etc/dbus-serialbattery/install-nightly.sh | 3 +++ etc/dbus-serialbattery/installble.sh | 4 +++- etc/dbus-serialbattery/installlocal.sh | 4 +++- etc/dbus-serialbattery/installqml.sh | 4 +++- etc/dbus-serialbattery/installrelease.sh | 4 +++- etc/dbus-serialbattery/reinstalllocal.sh | 4 +++- etc/dbus-serialbattery/restart-driver-and-ble.sh | 6 +++++- etc/dbus-serialbattery/restartservice.sh | 8 ++++++-- etc/dbus-serialbattery/restoregui.sh | 9 ++++++--- etc/dbus-serialbattery/start-serialbattery.sh | 4 +++- 11 files changed, 41 insertions(+), 13 deletions(-) diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh index 28afde26..8450cb8e 100644 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disabledriver.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x DRIVERNAME=dbus-serialbattery diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index d39d4265..1b909e0d 100644 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -1,5 +1,8 @@ #!/bin/bash +# remove comment for easier troubleshooting +#set -x + PS3="Select the branch you want to install the unreleased code (possible bugs included): " select branch in master jkbms_ble quit diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index aa93006a..d38094d1 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x # install required packages # TO DO: Check first if packages are already installed diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 7f5e4acf..47f8db91 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -1,5 +1,7 @@ #!/bin/sh -set -x + +# remove comment for easier troubleshooting +#set -x # extract driver tar -zxf ./venus-data.tar.gz -C /data diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index ed8ce289..5659652c 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x # elaborate version string for better comparing # https://github.com/kwindrem/SetupHelper/blob/ebaa65fcf23e2bea6797f99c1c41174143c1153c/updateFileSets#L56-L81 diff --git a/etc/dbus-serialbattery/installrelease.sh b/etc/dbus-serialbattery/installrelease.sh index 5bb32ce9..1b5cb6ee 100644 --- a/etc/dbus-serialbattery/installrelease.sh +++ b/etc/dbus-serialbattery/installrelease.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x # download latest release curl -s https://api.github.com/repos/Louisvdw/dbus-serialbattery/releases/latest | grep "browser_download_url.*gz" | cut -d : -f 2,3 | tr -d \" | wget -O venus-data.tar.gz -qi - diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 00f96c3a..2c66f9dd 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x DRIVERNAME=dbus-serialbattery diff --git a/etc/dbus-serialbattery/restart-driver-and-ble.sh b/etc/dbus-serialbattery/restart-driver-and-ble.sh index 441dedf7..d85d1c3d 100644 --- a/etc/dbus-serialbattery/restart-driver-and-ble.sh +++ b/etc/dbus-serialbattery/restart-driver-and-ble.sh @@ -1,4 +1,8 @@ -#!/bin/sh +#!/bin/bash + +# remove comment for easier troubleshooting +#set -x + sh /data/etc/dbus-serialbattery/reinstalllocal.sh sh /data/etc/dbus-serialbattery/restartservice.sh diff --git a/etc/dbus-serialbattery/restartservice.sh b/etc/dbus-serialbattery/restartservice.sh index 51ecf027..8edcb59a 100755 --- a/etc/dbus-serialbattery/restartservice.sh +++ b/etc/dbus-serialbattery/restartservice.sh @@ -1,2 +1,6 @@ -#!/bin/sh -svc -d -u /service/dbus-serialbattery +#!/bin/bash + +# remove comment for easier troubleshooting +#set -x + +svc -d -u /service/dbus-serialbattery diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restoregui.sh index 2dae5336..7a59c9c9 100644 --- a/etc/dbus-serialbattery/restoregui.sh +++ b/etc/dbus-serialbattery/restoregui.sh @@ -1,11 +1,14 @@ #!/bin/bash -set -x -#restore original backup + +# remove comment for easier troubleshooting +#set -x + +# restore original backup if [ -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then cp -f /opt/victronenergy/gui/qml/PageBattery.qml.backup /opt/victronenergy/gui/qml/PageBattery.qml echo "PageBattery.qml was restored." fi -# backup old PageLynxIonIo.qml once. New firmware upgrade will remove the backup +# restore original backup if [ -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then cp -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup /opt/victronenergy/gui/qml/PageLynxIonIo.qml echo "PageLynxIonIo.qml was restored." diff --git a/etc/dbus-serialbattery/start-serialbattery.sh b/etc/dbus-serialbattery/start-serialbattery.sh index 38de1d58..6f29f840 100644 --- a/etc/dbus-serialbattery/start-serialbattery.sh +++ b/etc/dbus-serialbattery/start-serialbattery.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x . /opt/victronenergy/serial-starter/run-service.sh From efd555510acbfa584ffddecda0d75e8685880763 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 12:24:55 +0200 Subject: [PATCH 061/209] added post install notes --- etc/dbus-serialbattery/installlocal.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 47f8db91..2fa49502 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -9,14 +9,17 @@ tar -zxf ./venus-data.tar.gz -C /data # install driver sh /data/etc/dbus-serialbattery/reinstalllocal.sh +echo echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." echo echo "BLUETOOTH battery connection: There are a few more steps to complete installation." -echo "1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." -echo "2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." -echo "3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create" -echo "ATTENTION!" -echo "- If you changed the default connection PIN of JKBMS, then you have to pair the JKBMS first using OS tools like the \"bluetoothctl\"." -echo "See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." +echo " 1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo " 2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." +echo " 3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." +echo " ATTENTION!" +echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." +echo " See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." echo echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"config.default.ini\" and add them to \"config.ini\" to persist future driver updates." +echo +echo From 60ddcf2a2b5b600abfebce19ea452b8874c20208 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 13:05:42 +0200 Subject: [PATCH 062/209] corrected errors --- etc/dbus-serialbattery/installble.sh | 7 ++++++- etc/dbus-serialbattery/uninstall.sh | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index d38094d1..abe86010 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -13,7 +13,12 @@ pip3 install bleak grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root # add install-script to rc.local to be ready for firmware update -grep -qxF "sh /data/etc/$DRIVERNAME/installble.sh" $filename || echo "sh /data/etc/$DRIVERNAME/installble.sh" >> $filename +filename=/data/rc.local +if [ ! -f $filename ]; then + echo "#!/bin/bash" >> $filename + chmod 755 $filename +fi +grep -qxF "sh /data/etc/dbus-serialbattery/installble.sh" $filename || echo "sh /data/etc/dbus-serialbattery/installble.sh" >> $filename install_service() { mkdir -p /service/dbus-blebattery-$1/log diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index 3d44bcec..c6309392 100644 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -1,5 +1,7 @@ #!/bin/bash -set -x + +# remove comment for easier troubleshooting +#set -x DRIVERNAME=dbus-serialbattery From c3a50a4f5e8be366c5b8741cf2e62b2ccd9b3b5d Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 13:32:09 +0200 Subject: [PATCH 063/209] fix black lint errors --- .github/workflows/analyse.yml | 6 ++ .github/workflows/release.yml | 2 + etc/dbus-serialbattery/battery.py | 72 ++++++++++++-------- etc/dbus-serialbattery/dbus-serialbattery.py | 4 +- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml index 8d027e13..41cf69f7 100644 --- a/.github/workflows/analyse.yml +++ b/.github/workflows/analyse.yml @@ -13,21 +13,27 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + - name: Set up Python environment uses: actions/setup-python@v4 with: python-version: "3.8.13" + - name: Clone velib_python and add it to PYTHONPATH for subsequent steps run: | git clone https://github.com/victronenergy/velib_python.git echo PYTHONPATH=$PYTHONPATH:$(pwd)/velib_python >> $GITHUB_ENV + - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: python + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 + - name: Execute black lint check uses: psf/black@stable + - name: flake8 Lint uses: py-actions/flake8@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c54513bb..c1213a5f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: build release archive run: | find . -type f -name "*.py" -exec chmod +x {} \; @@ -25,6 +26,7 @@ jobs: --exclude test_max17853.py \ conf/serial-starter.d \ etc/dbus-serialbattery/ + - name: Release uses: softprops/action-gh-release@v1 with: diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 5abce0b1..b4a97fbc 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -141,7 +141,7 @@ def to_temp(self, sensor: int, value: float) -> None: self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) - if sensor == 'mos': + if sensor == "mos": self.temp_mos = min(max(value, -20), 100) def manage_charge_voltage(self) -> None: @@ -200,25 +200,27 @@ def manage_charge_voltage_linear(self) -> None: self.max_voltage_start_time = None # INFO: battery will only switch to Absorption if all cells are balanced.reach MAC_CELL_VOLTAGE * cell count if they are all balanced. - if ( - foundHighCellVoltage - and self.allow_max_voltage - ): + if foundHighCellVoltage and self.allow_max_voltage: # set CVL only once every PENALTY_RECALCULATE_EVERY seconds control_voltage_time = int(time() / utils.PENALTY_RECALCULATE_EVERY) if control_voltage_time != self.control_voltage_last_set: # Keep penalty above min battery voltage - self.control_voltage = round( max( - voltageSum - penaltySum, - utils.MIN_CELL_VOLTAGE * self.cell_count, - ), 3) + self.control_voltage = round( + max( + voltageSum - penaltySum, + utils.MIN_CELL_VOLTAGE * self.cell_count, + ), + 3, + ) self.control_voltage_last_set = control_voltage_time elif self.allow_max_voltage: - self.control_voltage = round( (utils.MAX_CELL_VOLTAGE * self.cell_count), 3) + self.control_voltage = round((utils.MAX_CELL_VOLTAGE * self.cell_count), 3) else: - self.control_voltage = round( (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3) + self.control_voltage = round( + (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3 + ) def manage_charge_voltage_step(self) -> None: """ @@ -228,7 +230,6 @@ def manage_charge_voltage_step(self) -> None: voltageSum = 0 tDiff = 0 if utils.CVCM_ENABLE: - # calculate battery sum for i in range(self.cell_count): voltage = self.get_cell_voltage(i) @@ -281,7 +282,7 @@ def manage_charge_current(self) -> None: if utils.CCCM_T_ENABLE: charge_limits.append(self.calcMaxChargeCurrentReferringToTemperature()) - self.control_charge_current = round( min(charge_limits), 3) + self.control_charge_current = round(min(charge_limits), 3) if self.control_charge_current == 0: self.control_allow_charge = False @@ -494,7 +495,7 @@ def get_capacity_remain(self) -> Union[float, None]: return self.capacity * self.soc / 100 return None - def get_timeToSoc(self, socnum, crntPrctPerSec, onlyNumber = False) -> str: + def get_timeToSoc(self, socnum, crntPrctPerSec, onlyNumber=False) -> str: if self.current > 0: diffSoc = socnum - self.soc else: @@ -517,7 +518,7 @@ def get_timeToSoc(self, socnum, crntPrctPerSec, onlyNumber = False) -> str: return ttgStr - def get_secondsToString(self, timespan, precision = 3) -> str: + def get_secondsToString(self, timespan, precision=3) -> str: """ Transforms seconds to a string in the format: 1d 1h 1m 1s (Victron Style) :param precision: @@ -529,17 +530,17 @@ def get_secondsToString(self, timespan, precision = 3) -> str: This was added, since timedelta() returns strange values, if time is negative e.g.: seconds: -70245 --> timedelta output: -1 day, 4:29:15 --> calculation: -1 day + 4:29:15 --> real value -19:30:45 """ - tmp = '' if timespan >= 0 else '-' + tmp = "" if timespan >= 0 else "-" timespan = abs(timespan) m, s = divmod(timespan, 60) h, m = divmod(m, 60) d, h = divmod(h, 24) - tmp += ( ( str(d) + 'd ' ) if d > 0 else '' ) - tmp += ( ( str(h) + 'h ' ) if precision >= 1 and h > 0 else '' ) - tmp += ( ( str(m) + 'm ' ) if precision >= 2 and m > 0 else '' ) - tmp += ( ( str(s) + 's ' ) if precision == 3 and s > 0 else '' ) + tmp += (str(d) + "d ") if d > 0 else "" + tmp += (str(h) + "h ") if precision >= 1 and h > 0 else "" + tmp += (str(m) + "m ") if precision >= 2 and m > 0 else "" + tmp += (str(s) + "s ") if precision == 3 and s > 0 else "" return tmp.rstrip() @@ -667,18 +668,31 @@ def log_cell_data(self) -> bool: return True def log_settings(self) -> None: - cell_counter = len(self.cells) logger.info(f"Battery {self.type} connected to dbus from {self.port}") - logger.info( "========== Settings ==========") - logger.info(f"> Connection voltage: {self.voltage}V | Current: {self.current}A | SoC: {self.soc}%") - logger.info(f"> Cell count: {self.cell_count} | Cells populated: {cell_counter}") + logger.info("========== Settings ==========") + logger.info( + f"> Connection voltage: {self.voltage}V | Current: {self.current}A | SoC: {self.soc}%" + ) + logger.info( + f"> Cell count: {self.cell_count} | Cells populated: {cell_counter}" + ) logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") - logger.info(f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V") + logger.info( + f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" + ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") - logger.info(f"> MIN CELL VOLTAGE: {utils.MIN_CELL_VOLTAGE}V | MAX CELL VOLTAGE: {utils.MAX_CELL_VOLTAGE}V") - logger.info(f"> CCCM CV: {str(utils.CCCM_CV_ENABLE).ljust(5)} | DCCM CV: {utils.DCCM_CV_ENABLE}") - logger.info(f"> CCCM T: {str(utils.CCCM_T_ENABLE).ljust(5)} | DCCM T: {utils.DCCM_T_ENABLE}") - logger.info(f"> CCCM SOC: {str(utils.CCCM_SOC_ENABLE).ljust(5)} | DCCM SOC: {utils.DCCM_SOC_ENABLE}") + logger.info( + f"> MIN CELL VOLTAGE: {utils.MIN_CELL_VOLTAGE}V | MAX CELL VOLTAGE: {utils.MAX_CELL_VOLTAGE}V" + ) + logger.info( + f"> CCCM CV: {str(utils.CCCM_CV_ENABLE).ljust(5)} | DCCM CV: {utils.DCCM_CV_ENABLE}" + ) + logger.info( + f"> CCCM T: {str(utils.CCCM_T_ENABLE).ljust(5)} | DCCM T: {utils.DCCM_T_ENABLE}" + ) + logger.info( + f"> CCCM SOC: {str(utils.CCCM_SOC_ENABLE).ljust(5)} | DCCM SOC: {utils.DCCM_SOC_ENABLE}" + ) return diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index a03293e6..1a95c3b9 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -92,9 +92,7 @@ def get_port() -> str: logger.info("No Port needed") return "/dev/tty/USB9" - logger.info( - "dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION - ) + logger.info("dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION) port = get_port() battery = None From fd1894a6f6e3508e746547b065ab725dd66ff547 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 15:01:03 +0200 Subject: [PATCH 064/209] fix black lint errors --- etc/dbus-serialbattery/battery.py | 13 +++++++--- etc/dbus-serialbattery/dbus-serialbattery.py | 8 +++--- etc/dbus-serialbattery/jkbms_ble.py | 19 +------------- etc/dbus-serialbattery/jkbms_brn.py | 5 ++-- etc/dbus-serialbattery/minimalmodbus.py | 1 - etc/dbus-serialbattery/utils.py | 27 ++++++++++++-------- 6 files changed, 34 insertions(+), 39 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index b4a97fbc..ec91740c 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -199,7 +199,8 @@ def manage_charge_voltage_linear(self) -> None: self.allow_max_voltage = False self.max_voltage_start_time = None - # INFO: battery will only switch to Absorption if all cells are balanced.reach MAC_CELL_VOLTAGE * cell count if they are all balanced. + # INFO: battery will only switch to Absorption, if all cells are balanced. + # Reach MAX_CELL_VOLTAGE * cell count if they are all balanced. if foundHighCellVoltage and self.allow_max_voltage: # set CVL only once every PENALTY_RECALCULATE_EVERY seconds control_voltage_time = int(time() / utils.PENALTY_RECALCULATE_EVERY) @@ -302,7 +303,7 @@ def manage_charge_current(self) -> None: self.calcMaxDischargeCurrentReferringToTemperature() ) - self.control_discharge_current = round( min(discharge_limits), 3) + self.control_discharge_current = round(min(discharge_limits), 3) if self.control_discharge_current == 0: self.control_allow_discharge = False @@ -528,7 +529,10 @@ def get_secondsToString(self, timespan, precision=3) -> str: 3 = 1d 1h 1m 1s This was added, since timedelta() returns strange values, if time is negative - e.g.: seconds: -70245 --> timedelta output: -1 day, 4:29:15 --> calculation: -1 day + 4:29:15 --> real value -19:30:45 + e.g.: seconds: -70245 + --> timedelta output: -1 day, 4:29:15 + --> calculation: -1 day + 4:29:15 + --> real value -19:30:45 """ tmp = "" if timespan >= 0 else "-" timespan = abs(timespan) @@ -679,7 +683,8 @@ def log_settings(self) -> None: ) logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") logger.info( - f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" + f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | " + + "MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 1a95c3b9..7df68349 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -92,7 +92,9 @@ def get_port() -> str: logger.info("No Port needed") return "/dev/tty/USB9" - logger.info("dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION) + logger.info( + "dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION + ) port = get_port() battery = None @@ -100,9 +102,7 @@ def get_port() -> str: class_ = eval(port) testbms = class_("", 9600, sys.argv[2]) if testbms.test_connection() is True: - logger.info( - "Connection established to " + testbms.__class__.__name__ - ) + logger.info("Connection established to " + testbms.__class__.__name__) battery = testbms else: battery = get_battery_type(port) diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index 369ef848..f7d0496c 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -43,7 +43,6 @@ def test_connection(self): logger.error("no BMS found at " + self.jk.address) return False - """ # before indipended service, has to be checked @@ -87,7 +86,6 @@ def test_connection(self): self.jk.stop_scraping() return False - if not status["device_info"]["vendor_id"].startswith(("JK-", "JK_")): self.jk.stop_scraping() return False @@ -166,7 +164,7 @@ def refresh_data(self): self.balancing_current = ( st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 - else ( 65536/1000 - st["cell_info"]["balancing_current"] ) * -1 + else (65536/1000 - st["cell_info"]["balancing_current"]) * -1 ) self.balancing_action = st["cell_info"]["balancing_action"] @@ -232,18 +230,3 @@ def reset_bluetooth(self): def get_balancing(self): return 1 if self.balancing else 0 - - - def reset_bluetooth(self): - logger.info("reset of bluetooth triggered") - self.resetting = True - # if self.jk.is_running(): - # self.jk.stop_scraping() - logger.info("scraping ended, issuing sys-commands") - os.system("kill -9 $(pidof bluetoothd)") - # os.system("/etc/init.d/bluetooth stop") is not enugh, kill -9 via pid is needed - time.sleep(2) - os.system("rfkill block bluetooth") - os.system("rfkill unblock bluetooth") - os.system("/etc/init.d/bluetooth start") - logger.info("bluetooth should have been restarted") diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index d20ceeb0..a3db8d1f 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -330,7 +330,7 @@ async def asy_connect_and_scrape(self): await self.request_bt("cell_info", client) # await self.enable_charging(client) - last_dev_info = time.time() + # last_dev_info = time.time() while client.is_connected and self.run and self.main_thread.is_alive(): await asyncio.sleep(0.01) except Exception as e: @@ -341,7 +341,7 @@ async def asy_connect_and_scrape(self): try: await client.disconnect() except Exception as e: - info("error while disconnecting") + info("error while disconnecting: " + str(e)) print("Exiting bt-loop") @@ -379,6 +379,7 @@ async def enable_charging(self, c): await self.write_register(0x1F, b"\x01\x00\x00\x00", 4, c) await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) + ''' if __name__ == "__main__": jk = JkBmsBle("C8:47:8C:E4:54:0E") diff --git a/etc/dbus-serialbattery/minimalmodbus.py b/etc/dbus-serialbattery/minimalmodbus.py index dc00434b..3519be07 100644 --- a/etc/dbus-serialbattery/minimalmodbus.py +++ b/etc/dbus-serialbattery/minimalmodbus.py @@ -1812,7 +1812,6 @@ def _extract_payload( ) if mode == MODE_ASCII: - # Validate the ASCII header and footer. if response[_BYTEPOSITION_FOR_ASCII_HEADER] != _ASCII_HEADER: raise InvalidResponseError( diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index b56904c0..6c952ce0 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -42,8 +42,10 @@ def _get_list_from_config( # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps -# True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) -# For CVL the penalties are only applied, if the cell voltage reaches the penalty voltage +# True is a linear mode. For CCL and DCL the values between the steps are calculated for +# smoother values (by WaldemarFech) +# For CVL the penalties are only applied, if the cell voltage reaches +# the penalty voltage LINEAR_LIMITATION_ENABLE = "True" == config["DEFAULT"]["LINEAR_LIMITATION_ENABLE"] # Battery Current limits @@ -52,11 +54,12 @@ def _get_list_from_config( # --------- Charge Voltage limitation (affecting CVL) --------- -# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) -# after max voltage is reached for MAX_VOLTAGE_TIME_SEC. It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT +# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to +# float voltage (FLOAT_CELL_VOLTAGE * cell count) after max voltage is reached for MAX_VOLTAGE_TIME_SEC. +# It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT. # If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied -# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. -# Allow max voltage of 55.2V again, if SoC is once below 90% +# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float +# voltage of 53.6V to don't stress the batteries. Allow max voltage of 55.2V again, if SoC is once below 90% # Charge voltage control management enable (True/False). CVCM_ENABLE = "True" == config["DEFAULT"]["CVCM_ENABLE"] @@ -74,8 +77,9 @@ def _get_list_from_config( # If the cell voltage reaches 3.48V, then reduce actual battery-voltage by 0.01V # If the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded # There will be a sum of all penalties for each cell, which exceeds the limits -# NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of PENALTY_BATTERY_VOLTAGE, -# else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. +# NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of +# PENALTY_BATTERY_VOLTAGE, else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage +# has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. PENALTY_AT_CELL_VOLTAGE = _get_list_from_config( "DEFAULT", "PENALTY_AT_CELL_VOLTAGE", @@ -211,7 +215,11 @@ def _get_list_from_config( TIME_TO_SOC_VALUE_TYPE = int(config["DEFAULT"]["TIME_TO_SOC_VALUE_TYPE"]) # Specify in seconds how often the TimeToSoc should be recalculated # Minimum are 5 seconds to prevent CPU overload -TIME_TO_SOC_RECALCULATE_EVERY = int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) if int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) > 5 else 5 +TIME_TO_SOC_RECALCULATE_EVERY = ( + int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) + if int(config["DEFAULT"]["TIME_TO_SOC_RECALCULATE_EVERY"]) > 5 + else 5 +) # Include TimeToSoC points when moving away from the SoC point [Valid values True, False] # These will be as negative time. Disabling this improves performance slightly TIME_TO_SOC_INC_FROM = "True" == config["DEFAULT"]["TIME_TO_SOC_INC_FROM"] @@ -257,7 +265,6 @@ def _get_list_from_config( LIPRO_CELL_COUNT = int(config["DEFAULT"]["LIPRO_CELL_COUNT"]) - # --------- Functions --------- def constrain(val, min_val, max_val): if min_val > max_val: From 59d91e7ad222af3227842082d31426b5a36728af Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 15:13:56 +0200 Subject: [PATCH 065/209] fix black lint errors --- etc/dbus-serialbattery/battery.py | 4 +- etc/dbus-serialbattery/dbushelper.py | 80 ++++++++++++++++--------- etc/dbus-serialbattery/jkbms.py | 6 +- etc/dbus-serialbattery/jkbms_ble.py | 2 +- etc/dbus-serialbattery/jkbms_brn.py | 15 +++-- etc/dbus-serialbattery/util_max17853.py | 1 - etc/dbus-serialbattery/utils.py | 49 +++++++++------ 7 files changed, 101 insertions(+), 56 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index ec91740c..555b2757 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -683,8 +683,8 @@ def log_settings(self) -> None: ) logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") logger.info( - f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | " + - "MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" + f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | " + + "MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 6cfc3260..89256feb 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -298,16 +298,15 @@ def setup_vedbus(self): ) # Create TimeToSoC items only if enabled - if ( - self.battery.capacity is not None - and len(TIME_TO_SOC_POINTS) > 0 - ): + if self.battery.capacity is not None and len(TIME_TO_SOC_POINTS) > 0: # Create TimeToGo item self._dbusservice.add_path("/TimeToGo", None, writeable=True) # Create TimeToSoc items for num in TIME_TO_SOC_POINTS: - self._dbusservice.add_path("/TimeToSoC/" + str(num), None, writeable=True) + self._dbusservice.add_path( + "/TimeToSoC/" + str(num), None, writeable=True + ) logger.info(f"publish config values = {PUBLISH_CONFIG_VALUES}") if PUBLISH_CONFIG_VALUES == 1: @@ -346,7 +345,6 @@ def publish_battery(self, loop): loop.quit() def publish_dbus(self): - # Update SOC, DC and System items self._dbusservice["/System/NrOfCellsPerBattery"] = self.battery.cell_count self._dbusservice["/Soc"] = round(self.battery.soc, 2) @@ -380,9 +378,7 @@ def publish_dbus(self): if self.battery.discharge_fet and self.battery.control_allow_discharge else 0 ) - self._dbusservice["/Io/AllowToBalance"] = ( - 1 if self.battery.balance_fet else 0 - ) + self._dbusservice["/Io/AllowToBalance"] = 1 if self.battery.balance_fet else 0 self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None @@ -401,8 +397,12 @@ def publish_dbus(self): self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control - self._dbusservice["/Info/MaxChargeCurrent"] = self.battery.control_charge_current - self._dbusservice["/Info/MaxDischargeCurrent"] = self.battery.control_discharge_current + self._dbusservice[ + "/Info/MaxChargeCurrent" + ] = self.battery.control_charge_current + self._dbusservice[ + "/Info/MaxDischargeCurrent" + ] = self.battery.control_discharge_current # Voltage control self._dbusservice["/Info/MaxChargeVoltage"] = self.battery.control_voltage @@ -410,23 +410,45 @@ def publish_dbus(self): # Updates from cells self._dbusservice["/System/MinVoltageCellId"] = self.battery.get_min_cell_desc() self._dbusservice["/System/MaxVoltageCellId"] = self.battery.get_max_cell_desc() - self._dbusservice["/System/MinCellVoltage"] = self.battery.get_min_cell_voltage() - self._dbusservice["/System/MaxCellVoltage"] = self.battery.get_max_cell_voltage() + self._dbusservice[ + "/System/MinCellVoltage" + ] = self.battery.get_min_cell_voltage() + self._dbusservice[ + "/System/MaxCellVoltage" + ] = self.battery.get_max_cell_voltage() self._dbusservice["/Balancing"] = self.battery.get_balancing() # Update the alarms self._dbusservice["/Alarms/LowVoltage"] = self.battery.protection.voltage_low - self._dbusservice["/Alarms/LowCellVoltage"] = self.battery.protection.voltage_cell_low + self._dbusservice[ + "/Alarms/LowCellVoltage" + ] = self.battery.protection.voltage_cell_low self._dbusservice["/Alarms/HighVoltage"] = self.battery.protection.voltage_high self._dbusservice["/Alarms/LowSoc"] = self.battery.protection.soc_low - self._dbusservice["/Alarms/HighChargeCurrent"] = self.battery.protection.current_over - self._dbusservice["/Alarms/HighDischargeCurrent"] = self.battery.protection.current_under - self._dbusservice["/Alarms/CellImbalance"] = self.battery.protection.cell_imbalance - self._dbusservice["/Alarms/InternalFailure"] = self.battery.protection.internal_failure - self._dbusservice["/Alarms/HighChargeTemperature"] = self.battery.protection.temp_high_charge - self._dbusservice["/Alarms/LowChargeTemperature"] = self.battery.protection.temp_low_charge - self._dbusservice["/Alarms/HighTemperature"] = self.battery.protection.temp_high_discharge - self._dbusservice["/Alarms/LowTemperature"] = self.battery.protection.temp_low_discharge + self._dbusservice[ + "/Alarms/HighChargeCurrent" + ] = self.battery.protection.current_over + self._dbusservice[ + "/Alarms/HighDischargeCurrent" + ] = self.battery.protection.current_under + self._dbusservice[ + "/Alarms/CellImbalance" + ] = self.battery.protection.cell_imbalance + self._dbusservice[ + "/Alarms/InternalFailure" + ] = self.battery.protection.internal_failure + self._dbusservice[ + "/Alarms/HighChargeTemperature" + ] = self.battery.protection.temp_high_charge + self._dbusservice[ + "/Alarms/LowChargeTemperature" + ] = self.battery.protection.temp_low_charge + self._dbusservice[ + "/Alarms/HighTemperature" + ] = self.battery.protection.temp_high_discharge + self._dbusservice[ + "/Alarms/LowTemperature" + ] = self.battery.protection.temp_low_discharge # cell voltages if BATTERY_CELL_DATA_FORMAT > 0: @@ -459,10 +481,8 @@ def publish_dbus(self): try: if ( self.battery.capacity is not None - and - len(TIME_TO_SOC_POINTS) > 0 - and - ( + and len(TIME_TO_SOC_POINTS) > 0 + and ( ( # update only once in same second int(time()) != self.battery.time_to_soc_update @@ -482,7 +502,13 @@ def publish_dbus(self): # Update TimeToGo item, has to be a positive int since it's used from dbus-systemcalc-py self._dbusservice["/TimeToGo"] = ( - abs ( int ( self.battery.get_timeToSoc(SOC_LOW_WARNING, crntPrctPerSec, True) ) ) + abs( + int( + self.battery.get_timeToSoc( + SOC_LOW_WARNING, crntPrctPerSec, True + ) + ) + ) if self.battery.current else None ) diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/jkbms.py index 655c29af..2f5ccb60 100644 --- a/etc/dbus-serialbattery/jkbms.py +++ b/etc/dbus-serialbattery/jkbms.py @@ -94,7 +94,7 @@ def read_status_data(self): # MOSFET temperature offset = cellbyte_count + 3 temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] - self.to_temp('mos', temp_mos if temp_mos < 99 else (100 - temp_mos)) + self.to_temp("mos", temp_mos if temp_mos < 99 else (100 - temp_mos)) offset = cellbyte_count + 12 voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0] @@ -148,7 +148,9 @@ def read_status_data(self): # show wich cells are balancing if self.get_min_cell() is not None and self.get_max_cell() is not None: for c in range(self.cell_count): - if self.balancing and ( self.get_min_cell() == c or self.get_max_cell() == c ): + if self.balancing and ( + self.get_min_cell() == c or self.get_max_cell() == c + ): self.cells[c].balance = True else: self.cells[c].balance = False diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/jkbms_ble.py index f7d0496c..ecc0cf35 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/jkbms_ble.py @@ -164,7 +164,7 @@ def refresh_data(self): self.balancing_current = ( st["cell_info"]["balancing_current"] if st["cell_info"]["balancing_current"] < 32768 - else (65536/1000 - st["cell_info"]["balancing_current"]) * -1 + else (65536 / 1000 - st["cell_info"]["balancing_current"]) * -1 ) self.balancing_action = st["cell_info"]["balancing_action"] diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index a3db8d1f..56079ff1 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -119,7 +119,14 @@ def translate(self, fb, translation, o, f32s=False, i=0): # handle raw bytes without unpack_from; # 3. param gives no format but number of bytes val = bytearray( - fb[translation[1] + i + offset: translation[1] + i + translation[2] + offset] + fb[ + translation[1] + + i + + offset : translation[1] + + i + + translation[2] + + offset + ] ) i += translation[2] else: @@ -171,7 +178,7 @@ def decode_device_info_jk02(self): def decode_cellinfo_jk02(self): fb = self.frame_buffer - has32s = (fb[189] == 0x00 and fb[189 + 32] > 0) + has32s = fb[189] == 0x00 and fb[189 + 32] > 0 for t in TRANSLATE_CELL_INFO: self.translate(fb, t, self.bms_status, f32s=has32s) self.decode_warnings(fb) @@ -380,11 +387,11 @@ async def enable_charging(self, c): await self.write_register(0x40, b"\x01\x00\x00\x00", 4, c) -''' +""" if __name__ == "__main__": jk = JkBmsBle("C8:47:8C:E4:54:0E") jk.start_scraping() while True: print(jk.get_status()) time.sleep(5) -''' +""" diff --git a/etc/dbus-serialbattery/util_max17853.py b/etc/dbus-serialbattery/util_max17853.py index 02e06e4a..caaacfbf 100644 --- a/etc/dbus-serialbattery/util_max17853.py +++ b/etc/dbus-serialbattery/util_max17853.py @@ -316,7 +316,6 @@ def v_cell_d(self): b_lim = False for index, v in enumerate(V_Cells): - if v > 3.55: b_lim = True if v > vc_max: diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 6c952ce0..2083440b 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -50,7 +50,9 @@ def _get_list_from_config( # Battery Current limits MAX_BATTERY_CHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_CHARGE_CURRENT"]) -MAX_BATTERY_DISCHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"]) +MAX_BATTERY_DISCHARGE_CURRENT = float( + config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"] +) # --------- Charge Voltage limitation (affecting CVL) --------- @@ -81,15 +83,11 @@ def _get_list_from_config( # PENALTY_BATTERY_VOLTAGE, else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage # has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. PENALTY_AT_CELL_VOLTAGE = _get_list_from_config( - "DEFAULT", - "PENALTY_AT_CELL_VOLTAGE", - lambda v: float(v) + "DEFAULT", "PENALTY_AT_CELL_VOLTAGE", lambda v: float(v) ) # this voltage will be subtracted PENALTY_BATTERY_VOLTAGE = _get_list_from_config( - "DEFAULT", - "PENALTY_BATTERY_VOLTAGE", - lambda v: float(v) + "DEFAULT", "PENALTY_BATTERY_VOLTAGE", lambda v: float(v) ) # Specify in seconds how often the penalty should be recalculated PENALTY_RECALCULATE_EVERY = int(config["DEFAULT"]["PENALTY_RECALCULATE_EVERY"]) @@ -98,7 +96,9 @@ def _get_list_from_config( # Reset max voltage after MAX_VOLTAGE_TIME_SEC = float(config["DEFAULT"]["MAX_VOLTAGE_TIME_SEC"]) # Specify SoC where CVL limit is reset to max voltage -SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = float(config["DEFAULT"]["SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT"]) +SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = float( + config["DEFAULT"]["SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT"] +) # --------- Cell Voltage Current limitation (affecting CCL/DCL) --------- @@ -146,9 +146,7 @@ def _get_list_from_config( # Set steps to reduce battery current # The current will be changed linear between those steps if LINEAR_LIMITATION_ENABLE is set to True TEMPERATURE_LIMITS_WHILE_CHARGING = _get_list_from_config( - "DEFAULT", - "TEMPERATURE_LIMITS_WHILE_CHARGING", - lambda v: float(v) + "DEFAULT", "TEMPERATURE_LIMITS_WHILE_CHARGING", lambda v: float(v) ) MAX_CHARGE_CURRENT_T = _get_list_from_config( "DEFAULT", @@ -181,9 +179,15 @@ def _get_list_from_config( CC_SOC_LIMIT3 = float(config["DEFAULT"]["CC_SOC_LIMIT3"]) # Charge current limits -CC_CURRENT_LIMIT1 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT1_FRACTION"]) -CC_CURRENT_LIMIT2 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT2_FRACTION"]) -CC_CURRENT_LIMIT3 = MAX_BATTERY_CHARGE_CURRENT * float(config["DEFAULT"]["CC_CURRENT_LIMIT3_FRACTION"]) +CC_CURRENT_LIMIT1 = MAX_BATTERY_CHARGE_CURRENT * float( + config["DEFAULT"]["CC_CURRENT_LIMIT1_FRACTION"] +) +CC_CURRENT_LIMIT2 = MAX_BATTERY_CHARGE_CURRENT * float( + config["DEFAULT"]["CC_CURRENT_LIMIT2_FRACTION"] +) +CC_CURRENT_LIMIT3 = MAX_BATTERY_CHARGE_CURRENT * float( + config["DEFAULT"]["CC_CURRENT_LIMIT3_FRACTION"] +) # Discharge current soc limits DC_SOC_LIMIT1 = float(config["DEFAULT"]["DC_SOC_LIMIT1"]) @@ -191,9 +195,15 @@ def _get_list_from_config( DC_SOC_LIMIT3 = float(config["DEFAULT"]["DC_SOC_LIMIT3"]) # Discharge current limits -DC_CURRENT_LIMIT1 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT1_FRACTION"]) -DC_CURRENT_LIMIT2 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT2_FRACTION"]) -DC_CURRENT_LIMIT3 = MAX_BATTERY_DISCHARGE_CURRENT * float(config["DEFAULT"]["DC_CURRENT_LIMIT3_FRACTION"]) +DC_CURRENT_LIMIT1 = MAX_BATTERY_DISCHARGE_CURRENT * float( + config["DEFAULT"]["DC_CURRENT_LIMIT1_FRACTION"] +) +DC_CURRENT_LIMIT2 = MAX_BATTERY_DISCHARGE_CURRENT * float( + config["DEFAULT"]["DC_CURRENT_LIMIT2_FRACTION"] +) +DC_CURRENT_LIMIT3 = MAX_BATTERY_DISCHARGE_CURRENT * float( + config["DEFAULT"]["DC_CURRENT_LIMIT3_FRACTION"] +) # --------- Time-To-Soc --------- @@ -207,7 +217,9 @@ def _get_list_from_config( # [Valid values 0-100, comma separated list. More that 20 intervals are not recommended] # Example: TIME_TO_SOC_POINTS = 100, 95, 90, 85, 75, 50, 25, 20, 10, 0 # Leave empty to disable -TIME_TO_SOC_POINTS = _get_list_from_config("DEFAULT", "TIME_TO_SOC_POINTS", lambda v: int(v)) +TIME_TO_SOC_POINTS = _get_list_from_config( + "DEFAULT", "TIME_TO_SOC_POINTS", lambda v: int(v) +) # Specify TimeToSoc value type [Valid values 1, 2, 3] # 1 Seconds # 2 Time string d h m s @@ -284,7 +296,6 @@ def calcLinearRelationship(inValue, inArray, outArray): if inArray[0] > inArray[-1]: # change compare-direction in array return calcLinearRelationship(inValue, inArray[::-1], outArray[::-1]) else: - # Handle out of bounds if inValue <= inArray[0]: return outArray[0] From 194b83f634d46b7371197d770a64e5860689e214 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 15:22:58 +0200 Subject: [PATCH 066/209] bump version --- etc/dbus-serialbattery/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 2083440b..69d0aabc 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230413)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230415)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 3219e7030481092fd49d19ab1558d9c67d556dc3 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 15 Apr 2023 17:36:56 +0200 Subject: [PATCH 067/209] fixed serial-starter.d https://github.com/Louisvdw/dbus-serialbattery/issues/520 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 ++ .github/workflows/release.yml | 1 - conf/serial-starter.d | 3 --- etc/dbus-serialbattery/disabledriver.sh | 2 +- etc/dbus-serialbattery/reinstalllocal.sh | 23 ++++++++++++++++++----- etc/dbus-serialbattery/uninstall.sh | 2 +- 6 files changed, 22 insertions(+), 11 deletions(-) delete mode 100644 conf/serial-starter.d diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3efc3b1f..224f77d4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -34,3 +34,5 @@ If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. + +Please add multiline code or logfiles by useing three backticks (```) or three tildes (~~~) on the lines before and after the code block. See https://www.markdownguide.org/extended-syntax/#fenced-code-blocks for more details. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1213a5f..0b6a57da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,6 @@ jobs: --exclude restartservice.sh \ --exclude revov.py \ --exclude test_max17853.py \ - conf/serial-starter.d \ etc/dbus-serialbattery/ - name: Release diff --git a/conf/serial-starter.d b/conf/serial-starter.d deleted file mode 100644 index e25054dc..00000000 --- a/conf/serial-starter.d +++ /dev/null @@ -1,3 +0,0 @@ -service sbattery dbus-serialbattery -alias default gps:vedirect:sbattery -alias rs485 cgwacs:fzsonick:imt:modbus:sbattery diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh index 8450cb8e..ca81d230 100644 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disabledriver.sh @@ -9,7 +9,7 @@ DRIVERNAME=dbus-serialbattery sh /opt/victronenergy/swupdate-scripts/remount-rw.sh # remove files -rm -f /data/conf/serial-starter.d +rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf rm -rf /service/dbus-blebattery-* # kill if running diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 2c66f9dd..66f5b042 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -18,11 +18,24 @@ cp -rf /data/etc/$DRIVERNAME/service /opt/victronenergy/service-templates/$DRIVE sh /data/etc/$DRIVERNAME/installqml.sh # check if serial-starter.d was deleted -serialstarter=/data/conf/serial-starter.d -if [ ! -f $serialstarter ]; then - echo "service sbattery dbus-serialbattery" >> $serialstarter - echo "alias default gps:vedirect:sbattery" >> $serialstarter - echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" >> $serialstarter +serialstarter_path="/data/conf/serial-starter.d" +serialstarter_file="$serialstarter_path/dbus-serialbattery.conf" + +# check if folder is a file (older versions of this driver) +if [ -f $serialstarter_path ]; then + rm -f $serialstarter_path +fi + +# check if folder exists +if [ ! -d $serialstarter_path ]; then + mkdir $serialstarter_path +fi + +# check if file exists +if [ ! -f $serialstarter_file ]; then + echo "service sbattery dbus-serialbattery" >> $serialstarter_file + echo "alias default gps:vedirect:sbattery" >> $serialstarter_file + echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" >> $serialstarter_file fi # restart if running diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index c6309392..af8083cd 100644 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -9,7 +9,7 @@ DRIVERNAME=dbus-serialbattery sh /opt/victronenergy/swupdate-scripts/remount-rw.sh # remove files -rm -f /data/conf/serial-starter.d +rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf rm -rf /opt/victronenergy/service/$DRIVERNAME rm -rf /opt/victronenergy/service-templates/$DRIVERNAME rm -rf /opt/victronenergy/$DRIVERNAME From 144d46b5f0860fe8e0bb2510843d2ae2d348fe38 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 18 Apr 2023 07:11:36 +0200 Subject: [PATCH 068/209] fix errors, move install notes to show always --- etc/dbus-serialbattery/install-nightly.sh | 4 ++-- etc/dbus-serialbattery/installlocal.sh | 15 --------------- etc/dbus-serialbattery/reinstalllocal.sh | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index 1b909e0d..820bb992 100644 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -40,8 +40,8 @@ chmod +x /data/etc/dbus-serialbattery/*.py chmod +x /data/etc/dbus-serialbattery/service/run chmod +x /data/etc/dbus-serialbattery/service/log/run -bash /data/etc/dbus-serialbattery/install-local.sh +bash /data/etc/dbus-serialbattery/reinstalllocal.sh if [[ $branch == "jkbms_ble" ]]; then - nano /data/etc/dbus-serialbattery/install-ble.sh + nano /data/etc/dbus-serialbattery/installble.sh fi diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh index 2fa49502..a4cd70c1 100644 --- a/etc/dbus-serialbattery/installlocal.sh +++ b/etc/dbus-serialbattery/installlocal.sh @@ -8,18 +8,3 @@ tar -zxf ./venus-data.tar.gz -C /data # install driver sh /data/etc/dbus-serialbattery/reinstalllocal.sh - -echo -echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." -echo -echo "BLUETOOTH battery connection: There are a few more steps to complete installation." -echo " 1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." -echo " 2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." -echo " 3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." -echo " ATTENTION!" -echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." -echo " See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." -echo -echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"config.default.ini\" and add them to \"config.ini\" to persist future driver updates." -echo -echo diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 66f5b042..c562bfee 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -57,3 +57,19 @@ if [ ! -f $filename ]; then echo "; If you want to add custom settings, then check the settings you want to change in \"config.default.ini\"" >> $filename echo "; and add them below to persist future driver updates." >> $filename fi + +# install notes +echo +echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." +echo +echo "BLUETOOTH battery connection: There are a few more steps to complete installation." +echo " 1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo " 2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." +echo " 3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." +echo " ATTENTION!" +echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." +echo " See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." +echo +echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"config.default.ini\" and add them to \"config.ini\" to persist future driver updates." +echo +echo From 962d21dca50927c7a2a1d68a9f2ea7fb2b246da2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 18 Apr 2023 07:21:45 +0200 Subject: [PATCH 069/209] Update install-nightly.sh --- etc/dbus-serialbattery/install-nightly.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index 820bb992..3caaa18b 100644 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -33,7 +33,7 @@ cd /tmp wget https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip unzip $branch.zip -cp /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc +cp -rf /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc chmod +x /data/etc/dbus-serialbattery/*.sh chmod +x /data/etc/dbus-serialbattery/*.py From 008082cf70a34d589ddcb6e0d64aa59eb14e48d4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 18 Apr 2023 07:30:13 +0200 Subject: [PATCH 070/209] Update install-nightly.sh --- etc/dbus-serialbattery/install-nightly.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index 3caaa18b..19d6a4ee 100644 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -30,7 +30,10 @@ done cd /tmp -wget https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip +# clean already extracted folder +rm -rf /tmp/dbus-serialbattery-$branch + +wget -O $branch.zip https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip unzip $branch.zip cp -rf /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc @@ -42,6 +45,6 @@ chmod +x /data/etc/dbus-serialbattery/service/log/run bash /data/etc/dbus-serialbattery/reinstalllocal.sh -if [[ $branch == "jkbms_ble" ]]; then - nano /data/etc/dbus-serialbattery/installble.sh -fi +#if [[ $branch == "jkbms_ble" ]]; then +# nano /data/etc/dbus-serialbattery/installble.sh +#fi From 5e029bf806bdd79ee60e24cc259c34a5e4111b76 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 19 Apr 2023 07:37:32 +0200 Subject: [PATCH 071/209] make use of bluetooth configurable --- etc/dbus-serialbattery/config.default.ini | 3 +++ etc/dbus-serialbattery/dbus-serialbattery.py | 6 +++++- etc/dbus-serialbattery/install-nightly.sh | 2 +- etc/dbus-serialbattery/utils.py | 5 ++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 7bc2d501..4614487b 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -1,5 +1,8 @@ [DEFAULT] +; Enables the search/support for Bluetooth BMS +BLUETOOTH_ENABLED = False + ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 7df68349..ca47d99e 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -23,13 +23,17 @@ from daly import Daly from ant import Ant from jkbms import Jkbms -from jkbms_ble import Jkbms_Ble # from sinowealth import Sinowealth from renogy import Renogy from ecs import Ecs from lifepower import Lifepower +# import Bluetooth BMS classes +if utils.BLUETOOTH_ENABLED: + from jkbms_ble import Jkbms_Ble + + supported_bms_types = [ {"bms": LltJbd, "baud": 9600}, {"bms": Ant, "baud": 19200}, diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index 19d6a4ee..f59857ac 100644 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -3,7 +3,7 @@ # remove comment for easier troubleshooting #set -x -PS3="Select the branch you want to install the unreleased code (possible bugs included): " +PS3="Select the branch from wich you want to install the current code (possible bugs included): " select branch in master jkbms_ble quit do diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 69d0aabc..024aa81d 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,10 +36,13 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230415)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230419)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" +# Enables the search/support for Bluetooth BMS +BLUETOOTH_ENABLED = "True" == config["DEFAULT"]["BLUETOOTH_ENABLED"] + # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps # True is a linear mode. For CCL and DCL the values between the steps are calculated for From 94411e28e7e80013a40e0231bb08c0d84b189f7d Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 19 Apr 2023 07:53:20 +0200 Subject: [PATCH 072/209] remove modules with uninstall script --- etc/dbus-serialbattery/uninstall.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index af8083cd..90e75512 100644 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -24,3 +24,15 @@ sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local # remove cronjob sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root + +# uninstall modules +read -r -p "Do you also want to uninstall bleak, python3-pip and python3-modules? If you don't know select y. [Y/n] " response +echo +response=${response,,} # tolower +if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then + echo "Uninstalling modules..." + pip3 uninstall bleak + opkg remove python3-pip python3-modules + echo "done." + echo +fi From a695052420226f1bd88d8b546487e221e258eca0 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 19 Apr 2023 07:59:43 +0200 Subject: [PATCH 073/209] fix typo --- etc/dbus-serialbattery/dbus-serialbattery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index ca47d99e..824a44cc 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -109,7 +109,7 @@ def get_port() -> str: logger.info("Connection established to " + testbms.__class__.__name__) battery = testbms else: - battery = get_battery_type(port) + battery = get_battery(port) # exit if no battery could be found if battery is None: From 1ad14e65ea19749cd08295c67adaa5b771abee46 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 19 Apr 2023 09:44:28 +0200 Subject: [PATCH 074/209] Added install notes --- etc/dbus-serialbattery/reinstalllocal.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index c562bfee..6539e446 100644 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -63,9 +63,10 @@ echo echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." echo echo "BLUETOOTH battery connection: There are a few more steps to complete installation." -echo " 1. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." -echo " 2. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." -echo " 3. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." +echo " 1. Please enable Bluetooth in the config file by adding/changing \"BLUETOOTH_ENABLED = True\"." +echo " 2. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." +echo " 3. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." +echo " 4. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." echo " ATTENTION!" echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." echo " See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." From fe9cfdf59ae934c9d17a9f2dcdf01f540f4b6496 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 20 Apr 2023 13:16:45 +0200 Subject: [PATCH 075/209] fix #351 --- etc/dbus-serialbattery/lltjbd.py | 5 ++++- etc/dbus-serialbattery/utils.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index 9af90c02..2a3d536c 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -156,7 +156,10 @@ def read_gen_data(self): for t in range(self.temp_sensors): temp1 = unpack_from(">H", gen_data, 23 + (2 * t))[0] - self.to_temp(t + 1, kelvin_to_celsius(temp1 / 10)) + if t == 0: + self.to_temp("mos", kelvin_to_celsius(temp1 / 10)) + else: + self.to_temp(t, kelvin_to_celsius(temp1 / 10)) return True diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 024aa81d..8352a395 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230419)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230420)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 09639239d178798eab6b8b41e944e21ff9615b3a Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 20 Apr 2023 15:01:11 +0200 Subject: [PATCH 076/209] fix System Switch in IO page --- etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index 98ed18ce..e6ad7106 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -11,6 +11,7 @@ MbPage { description: qsTr("System Switch") bind: Utils.path(bindPrefix, "/SystemSwitch") readonly: true + show: item.valid possibleValues:[ MbOption{description: qsTr("Disabled"); value: 0}, MbOption{description: qsTr("Enabled"); value: 1} From 520eedc9fd4f24502ca23ce0b2a9900fb5fbeed2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 20 Apr 2023 17:04:35 +0200 Subject: [PATCH 077/209] fix #239 by @TimGFoley --- etc/dbus-serialbattery/renogy.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/renogy.py index 6f1c6c6f..5eadc35d 100644 --- a/etc/dbus-serialbattery/renogy.py +++ b/etc/dbus-serialbattery/renogy.py @@ -140,12 +140,30 @@ def read_cell_data(self): return True def read_temp_data(self): - temp1 = self.read_serial_data_renogy(self.command_bms_temp1) - temp2 = self.read_serial_data_renogy(self.command_bms_temp2) + # Check to see how many Enviromental Temp Sensors this battery has, it may have none. + num_env_temps = self.read_serial_data_renogy(self.command_env_temp_count) + logger.info('Number of Enviromental Sensors = %s', num_env_temps) + + if (num_env_temps == 0): + return False + + if (num_env_temps == 1): + temp1 = self.read_serial_data_renogy(self.command_env_temp1) + if temp1 is False: return False - self.temp1 = unpack(">H", temp1)[0] / 10 - self.temp2 = unpack(">H", temp2)[0] / 10 + else: + self.temp1 = unpack(">H", temp1)[0] / 10 + logger.info('temp1 = %s °C', temp1) + + if (num_env_temps == 2): + temp2 = self.read_serial_data_renogy(self.command_env_temp2) + + if temp2 is False: + return False + else: + self.temp2 = unpack(">H", temp2)[0] / 10 + logger.info('temp2 = %s °C', temp2) return True From 4ed644fa1ca3cc6695e550e24a84faaeb59f483a Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 20 Apr 2023 17:55:05 +0200 Subject: [PATCH 078/209] fix black lint errors --- etc/dbus-serialbattery/renogy.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/renogy.py index 5eadc35d..fe493da6 100644 --- a/etc/dbus-serialbattery/renogy.py +++ b/etc/dbus-serialbattery/renogy.py @@ -142,28 +142,28 @@ def read_cell_data(self): def read_temp_data(self): # Check to see how many Enviromental Temp Sensors this battery has, it may have none. num_env_temps = self.read_serial_data_renogy(self.command_env_temp_count) - logger.info('Number of Enviromental Sensors = %s', num_env_temps) + logger.info("Number of Enviromental Sensors = %s", num_env_temps) - if (num_env_temps == 0): + if num_env_temps == 0: return False - if (num_env_temps == 1): + if num_env_temps == 1: temp1 = self.read_serial_data_renogy(self.command_env_temp1) if temp1 is False: return False else: self.temp1 = unpack(">H", temp1)[0] / 10 - logger.info('temp1 = %s °C', temp1) + logger.info("temp1 = %s °C", temp1) - if (num_env_temps == 2): + if num_env_temps == 2: temp2 = self.read_serial_data_renogy(self.command_env_temp2) if temp2 is False: return False else: self.temp2 = unpack(">H", temp2)[0] / 10 - logger.info('temp2 = %s °C', temp2) + logger.info("temp2 = %s °C", temp2) return True From 13863f8abec5d7773d9fb0dbf18009f17ce2593d Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 21 Apr 2023 18:48:17 +0200 Subject: [PATCH 079/209] run code analyse only on code change --- .github/workflows/analyse.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/analyse.yml b/.github/workflows/analyse.yml index 41cf69f7..f6ae6184 100644 --- a/.github/workflows/analyse.yml +++ b/.github/workflows/analyse.yml @@ -5,6 +5,10 @@ on: branches: - '**' + # Run on changes in the etc folder + paths: + - etc/** + jobs: analyse: name: Analyze Using GitHub CodeQL From a4da321e0c8ad1f7b137adf9dff844cf3170e636 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 21 Apr 2023 19:01:00 +0200 Subject: [PATCH 080/209] fix #311 --- etc/dbus-serialbattery/battery.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 555b2757..a367d586 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -616,8 +616,10 @@ def get_midvoltage(self) -> Tuple[Union[float, None], Union[float, None]]: # get the midpoint of the battery midpoint = half1voltage + extra return ( - midpoint, - (half2voltage - half1voltage) / (half2voltage + half1voltage) * 100, + abs(midpoint), + abs( + (half2voltage - half1voltage) / (half2voltage + half1voltage) * 100 + ), ) except ValueError: return None, None From a32c73859bc0a5b23f56a2af549e9e6a74c70ae1 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 23 Apr 2023 15:14:46 +0200 Subject: [PATCH 081/209] Separate Time-To-Go and Time-To-SoC activation --- etc/dbus-serialbattery/config.default.ini | 8 +++- etc/dbus-serialbattery/dbushelper.py | 49 +++++++++++++---------- etc/dbus-serialbattery/utils.py | 6 ++- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 4614487b..a3351abb 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -10,8 +10,8 @@ BLUETOOTH_ENABLED = False LINEAR_LIMITATION_ENABLE = False ; Battery Current limits -MAX_BATTERY_CHARGE_CURRENT = 70.0 -MAX_BATTERY_DISCHARGE_CURRENT = 90.0 +MAX_BATTERY_CHARGE_CURRENT = 50.0 +MAX_BATTERY_DISCHARGE_CURRENT = 60.0 ; --------- Charge Voltage limitation (affecting CVL) --------- @@ -124,6 +124,10 @@ DC_CURRENT_LIMIT2_FRACTION = 0.3 DC_CURRENT_LIMIT3_FRACTION = 0.5 +; --------- Time-To-Go --------- +; Description: Calculates the time to go shown in the GUI +TIME_TO_GO_ENABLE = True + ; --------- Time-To-Soc --------- ; Description: Calculates the time to a specific SoC ; Example: TIME_TO_SOC_POINTS = 50, 25, 15, 0 diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 89256feb..bbbab8da 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -298,15 +298,17 @@ def setup_vedbus(self): ) # Create TimeToSoC items only if enabled - if self.battery.capacity is not None and len(TIME_TO_SOC_POINTS) > 0: + if self.battery.capacity is not None: # Create TimeToGo item - self._dbusservice.add_path("/TimeToGo", None, writeable=True) + if TIME_TO_GO_ENABLE: + self._dbusservice.add_path("/TimeToGo", None, writeable=True) # Create TimeToSoc items - for num in TIME_TO_SOC_POINTS: - self._dbusservice.add_path( - "/TimeToSoC/" + str(num), None, writeable=True - ) + if len(TIME_TO_SOC_POINTS) > 0: + for num in TIME_TO_SOC_POINTS: + self._dbusservice.add_path( + "/TimeToSoC/" + str(num), None, writeable=True + ) logger.info(f"publish config values = {PUBLISH_CONFIG_VALUES}") if PUBLISH_CONFIG_VALUES == 1: @@ -481,7 +483,7 @@ def publish_dbus(self): try: if ( self.battery.capacity is not None - and len(TIME_TO_SOC_POINTS) > 0 + and (TIME_TO_GO_ENABLE or len(TIME_TO_SOC_POINTS) > 0) and ( ( # update only once in same second @@ -500,27 +502,30 @@ def publish_dbus(self): abs(self.battery.current / (self.battery.capacity / 100)) / 3600 ) - # Update TimeToGo item, has to be a positive int since it's used from dbus-systemcalc-py - self._dbusservice["/TimeToGo"] = ( - abs( - int( - self.battery.get_timeToSoc( - SOC_LOW_WARNING, crntPrctPerSec, True + # Update TimeToGo item + if TIME_TO_GO_ENABLE: + # Update TimeToGo item, has to be a positive int since it's used from dbus-systemcalc-py + self._dbusservice["/TimeToGo"] = ( + abs( + int( + self.battery.get_timeToSoc( + SOC_LOW_WARNING, crntPrctPerSec, True + ) ) ) - ) - if self.battery.current - else None - ) - - # Update TimeToSoc items - for num in TIME_TO_SOC_POINTS: - self._dbusservice["/TimeToSoC/" + str(num)] = ( - self.battery.get_timeToSoc(num, crntPrctPerSec) if self.battery.current else None ) + # Update TimeToSoc items + if len(TIME_TO_SOC_POINTS) > 0: + for num in TIME_TO_SOC_POINTS: + self._dbusservice["/TimeToSoC/" + str(num)] = ( + self.battery.get_timeToSoc(num, crntPrctPerSec) + if self.battery.current + else None + ) + except: pass diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 8352a395..038b0527 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230420)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230423)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" @@ -209,6 +209,10 @@ def _get_list_from_config( ) +# --------- Time-To-Go --------- +# Description: Calculates the time to go shown in the GUI +TIME_TO_GO_ENABLE = "True" == config["DEFAULT"]["TIME_TO_GO_ENABLE"] + # --------- Time-To-Soc --------- # Description: Calculates the time to a specific SoC # Example: TIME_TO_SOC_POINTS = 50, 25, 15, 0 From 017df8a4ce7936910460fdf703853af7dd9094b0 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 25 Apr 2023 19:25:35 +0200 Subject: [PATCH 082/209] fix UnicodeDecodeError https://github.com/Louisvdw/dbus-serialbattery/discussions/485#discussioncomment-5694781 --- etc/dbus-serialbattery/config.default.ini | 1 + etc/dbus-serialbattery/jkbms_brn.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index a3351abb..160be98c 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -126,6 +126,7 @@ DC_CURRENT_LIMIT3_FRACTION = 0.5 ; --------- Time-To-Go --------- ; Description: Calculates the time to go shown in the GUI +; Recalculation is done based on TIME_TO_SOC_RECALCULATE_EVERY TIME_TO_GO_ENABLE = True ; --------- Time-To-Soc --------- diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 56079ff1..7826ba56 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -34,7 +34,9 @@ [["device_info", "sw_rev"], 30, "8s"], [["device_info", "uptime"], 38, " Date: Tue, 25 Apr 2023 20:45:03 +0200 Subject: [PATCH 083/209] fix UnicodeDecodeError https://github.com/Louisvdw/dbus-serialbattery/discussions/485#discussioncomment-5694781 --- etc/dbus-serialbattery/jkbms_brn.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/jkbms_brn.py index 7826ba56..25f434cb 100644 --- a/etc/dbus-serialbattery/jkbms_brn.py +++ b/etc/dbus-serialbattery/jkbms_brn.py @@ -34,9 +34,7 @@ [["device_info", "sw_rev"], 30, "8s"], [["device_info", "uptime"], 38, " Date: Wed, 26 Apr 2023 10:27:53 +0200 Subject: [PATCH 084/209] optimize uninstall script --- etc/dbus-serialbattery/uninstall.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index 90e75512..64ff5467 100644 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -3,24 +3,22 @@ # remove comment for easier troubleshooting #set -x -DRIVERNAME=dbus-serialbattery - # handle read only mounts sh /opt/victronenergy/swupdate-scripts/remount-rw.sh -# remove files -rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf -rm -rf /opt/victronenergy/service/$DRIVERNAME -rm -rf /opt/victronenergy/service-templates/$DRIVERNAME -rm -rf /opt/victronenergy/$DRIVERNAME +# remove files, don't use variables here, since on an error the whole /opt/victronenergy gets deleted +rm -f /data/conf/serial-starter.d/dbus-serialbattery.conf +rm -rf /opt/victronenergy/service/dbus-serialbattery +rm -rf /opt/victronenergy/service-templates/dbus-serialbattery +rm -rf /opt/victronenergy/dbus-serialbattery rm -rf /service/dbus-blebattery-* # kill if running -pkill -f "python .*/$DRIVERNAME.py" +pkill -f "python .*/dbus-serialbattery.py" # remove install-script from rc.local -sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local -sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local # remove cronjob sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root From 65a5ef2f00c02806686b7b91df2cdbda0df604a2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 10:28:54 +0200 Subject: [PATCH 085/209] fix typo in log_settings --- etc/dbus-serialbattery/battery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index a367d586..81b07266 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -686,7 +686,7 @@ def log_settings(self) -> None: logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") logger.info( f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | " - + "MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" + + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( From 4890ad119448df03a074565f96ad28dd330a71ea Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 10:31:52 +0200 Subject: [PATCH 086/209] add charge mode display --- etc/dbus-serialbattery/battery.py | 19 +++++++++++++++++++ etc/dbus-serialbattery/dbushelper.py | 2 ++ etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 6 ++++++ etc/dbus-serialbattery/utils.py | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 81b07266..b5c72a5b 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -82,6 +82,7 @@ def __init__(self, port, baud, address): self.control_charging = None self.control_voltage = None self.allow_max_voltage = True + self.charge_mode = None self.control_voltage_last_set = 0 self.max_voltage_start_time = None self.control_current = None @@ -214,14 +215,25 @@ def manage_charge_voltage_linear(self) -> None: 3, ) self.control_voltage_last_set = control_voltage_time + self.charge_mode = ( + "Bulk dynamic (linear mode)" + if self.max_voltage_start_time is None + else "Absorption dynamic (linear mode)" + ) elif self.allow_max_voltage: self.control_voltage = round((utils.MAX_CELL_VOLTAGE * self.cell_count), 3) + self.charge_mode = ( + "Bulk (linear mode)" + if self.max_voltage_start_time is None + else "Absorption (linear mode)" + ) else: self.control_voltage = round( (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3 ) + self.charge_mode = "Float (linear mode)" def manage_charge_voltage_step(self) -> None: """ @@ -270,8 +282,15 @@ def manage_charge_voltage_step(self) -> None: if self.allow_max_voltage: self.control_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.charge_mode = ( + "Bulk (step mode)" + if self.max_voltage_start_time is None + else "Absorption (step mode)" + ) + else: self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count + self.charge_mode = "Float (step mode)" def manage_charge_current(self) -> None: # Manage Charge Current Limitations diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index bbbab8da..2feceaa9 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -246,6 +246,7 @@ def setup_vedbus(self): self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) + self._dbusservice.add_path("/Io/ChargeMode", None, writeable=True) # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms @@ -381,6 +382,7 @@ def publish_dbus(self): else 0 ) self._dbusservice["/Io/AllowToBalance"] = 1 if self.battery.balance_fet else 0 + self._dbusservice["/Io/ChargeMode"] = self.battery.charge_mode self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index e6ad7106..a6eb59c3 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -18,6 +18,12 @@ MbPage { ] } + MbItemValue { + description: qsTr("Charge mode") + item.bind: service.path("/Io/ChargeMode") + show: item.valid + } + MbItemOptions { description: qsTr("Allow to charge") bind: Utils.path(bindPrefix, "/Io/AllowToCharge") diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 038b0527..7e8c63b6 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230423)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230426)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 9d993fc741eec14730bc4e808b70c14d8410a7c6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 14:44:51 +0200 Subject: [PATCH 087/209] Added: Temp name for sensor 1 & 2 Added the possibility to give a name to temperature sensor 1 & 2 This allows to see which sensor is low and high --- etc/dbus-serialbattery/battery.py | 12 ++++++++++++ etc/dbus-serialbattery/config.default.ini | 6 ++++++ etc/dbus-serialbattery/dbushelper.py | 7 ++++--- etc/dbus-serialbattery/utils.py | 6 ++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index b5c72a5b..1bd955a1 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -669,11 +669,23 @@ def get_min_temp(self) -> Union[float, None]: extractor=lambda temp1, temp2: min(temp1, temp2) ) + def get_min_temp_id(self) -> Union[str, None]: + if self.temp1 < self.temp2: + return utils.TEMP_1_NAME + else: + return utils.TEMP_2_NAME + def get_max_temp(self) -> Union[float, None]: return self.extract_from_temp_values( extractor=lambda temp1, temp2: max(temp1, temp2) ) + def get_max_temp_id(self) -> Union[str, None]: + if self.temp1 > self.temp2: + return utils.TEMP_1_NAME + else: + return utils.TEMP_2_NAME + def get_mos_temp(self) -> Union[float, None]: if self.temp_mos is not None: return self.temp_mos diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 160be98c..1861dfb0 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -172,6 +172,12 @@ BATTERY_CELL_DATA_FORMAT = 1 ; Simulate Midpoint graph (True/False). MIDPOINT_ENABLE = False +; Temperature sensor 1 name +TEMP_1_NAME = Temp 1 + +; Temperature sensor 2 name +TEMP_2_NAME = Temp 2 + ; --------- BMS specific settings --------- diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 2feceaa9..56eca32f 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -184,9 +184,6 @@ def setup_vedbus(self): writeable=True, gettextcallback=lambda p, v: "{:0.0f}Ah".format(v), ) - # Not used at this stage - # self._dbusservice.add_path('/System/MinTemperatureCellId', None, writeable=True) - # self._dbusservice.add_path('/System/MaxTemperatureCellId', None, writeable=True) # Create SOC, DC and System items self._dbusservice.add_path("/Soc", None, writeable=True) @@ -224,7 +221,9 @@ def setup_vedbus(self): # Create battery extras self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) + self._dbusservice.add_path('/System/MinTemperatureCellId', None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) + self._dbusservice.add_path('/System/MaxTemperatureCellId', None, writeable=True) self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", @@ -397,7 +396,9 @@ def publish_dbus(self): 0 if self.battery.online else 1 ) self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp() + self._dbusservice["/System/MinTemperatureCellId"] = self.battery.get_min_temp_id() self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp() + self._dbusservice["/System/MaxTemperatureCellId"] = self.battery.get_max_temp_id() self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 7e8c63b6..2b8f7c67 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -262,6 +262,12 @@ def _get_list_from_config( # Simulate Midpoint graph (True/False). MIDPOINT_ENABLE = "True" == config["DEFAULT"]["MIDPOINT_ENABLE"] +# Temperature sensor 1 name +TEMP_1_NAME = config["DEFAULT"]["TEMP_1_NAME"] + +# Temperature sensor 2 name +TEMP_2_NAME = config["DEFAULT"]["TEMP_2_NAME"] + # --------- BMS specific settings --------- From 80670453c84023629c33aee78003328796c0ec97 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 14:46:23 +0200 Subject: [PATCH 088/209] Added: Choose how battery temperature is assembled --- etc/dbus-serialbattery/battery.py | 13 ++++++++++--- etc/dbus-serialbattery/config.default.ini | 8 ++++++++ etc/dbus-serialbattery/utils.py | 7 +++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 1bd955a1..bd4b5b9f 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -660,9 +660,16 @@ def extract_from_temp_values(self, extractor) -> Union[float, None]: return None def get_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: round((float(temp1) + float(temp2)) / 2, 2) - ) + if utils.TEMP_BATTERY == 1: + return self.temp1 + elif utils.TEMP_BATTERY == 2: + return self.temp2 + else: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: round( + (float(temp1) + float(temp2)) / 2, 2 + ) + ) def get_min_temp(self) -> Union[float, None]: return self.extract_from_temp_values( diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 1861dfb0..91e823a5 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -172,6 +172,14 @@ BATTERY_CELL_DATA_FORMAT = 1 ; Simulate Midpoint graph (True/False). MIDPOINT_ENABLE = False + +; Battery temperature +; Specifiy how the battery temperature is assembled +; 0 Get mean of temperature sensor 1 and temperature sensor 2 +; 1 Get only temperature from temperature sensor 1 +; 2 Get only temperature from temperature sensor 2 +TEMP_BATTERY = 0 + ; Temperature sensor 1 name TEMP_1_NAME = Temp 1 diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 2b8f7c67..e1bbac75 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -262,6 +262,13 @@ def _get_list_from_config( # Simulate Midpoint graph (True/False). MIDPOINT_ENABLE = "True" == config["DEFAULT"]["MIDPOINT_ENABLE"] +# Battery temperature +# Specifiy how the battery temperature is assembled +# 0 Get mean of temp sensor 1 and temp sensor 2 +# 1 Get only temp from temp sensor 1 +# 2 Get only temp from temp sensor 2 +TEMP_BATTERY = int(config["DEFAULT"]["TEMP_BATTERY"]) + # Temperature sensor 1 name TEMP_1_NAME = config["DEFAULT"]["TEMP_1_NAME"] From 3777d50352fec0b88e4ef77a1ae07c57ad67b9b8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 14:48:36 +0200 Subject: [PATCH 089/209] Added/Changed alarms for JKBMS * Added: HighInternalTemperature alarm for JKBMS * Changed: Temperature alarm to not trigger all in the same condition --- etc/dbus-serialbattery/battery.py | 1 + etc/dbus-serialbattery/dbushelper.py | 18 ++++++++--- etc/dbus-serialbattery/jkbms.py | 48 ++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index bd4b5b9f..e28d5b5f 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -28,6 +28,7 @@ def __init__(self): self.temp_low_charge: int = None self.temp_high_discharge: int = None self.temp_low_discharge: int = None + self.temp_high_internal: int = None class Cell: diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 56eca32f..5a11deed 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -221,9 +221,9 @@ def setup_vedbus(self): # Create battery extras self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) - self._dbusservice.add_path('/System/MinTemperatureCellId', None, writeable=True) + self._dbusservice.add_path("/System/MinTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) - self._dbusservice.add_path('/System/MaxTemperatureCellId', None, writeable=True) + self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", @@ -264,6 +264,9 @@ def setup_vedbus(self): self._dbusservice.add_path("/Alarms/LowChargeTemperature", None, writeable=True) self._dbusservice.add_path("/Alarms/HighTemperature", None, writeable=True) self._dbusservice.add_path("/Alarms/LowTemperature", None, writeable=True) + self._dbusservice.add_path( + "/Alarms/HighInternalTemperature", None, writeable=True + ) # cell voltages if BATTERY_CELL_DATA_FORMAT > 0: @@ -396,9 +399,13 @@ def publish_dbus(self): 0 if self.battery.online else 1 ) self._dbusservice["/System/MinCellTemperature"] = self.battery.get_min_temp() - self._dbusservice["/System/MinTemperatureCellId"] = self.battery.get_min_temp_id() + self._dbusservice[ + "/System/MinTemperatureCellId" + ] = self.battery.get_min_temp_id() self._dbusservice["/System/MaxCellTemperature"] = self.battery.get_max_temp() - self._dbusservice["/System/MaxTemperatureCellId"] = self.battery.get_max_temp_id() + self._dbusservice[ + "/System/MaxTemperatureCellId" + ] = self.battery.get_max_temp_id() self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() # Charge control @@ -454,6 +461,9 @@ def publish_dbus(self): self._dbusservice[ "/Alarms/LowTemperature" ] = self.battery.protection.temp_low_discharge + self._dbusservice[ + "/Alarms/HighInternalTemperature" + ] = self.battery.protection.temp_high_internal # cell voltages if BATTERY_CELL_DATA_FORMAT > 0: diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/jkbms.py index 2f5ccb60..fd3e12bc 100644 --- a/etc/dbus-serialbattery/jkbms.py +++ b/etc/dbus-serialbattery/jkbms.py @@ -196,33 +196,63 @@ def get_max_cell(self): return max_cell def to_protection_bits(self, byte_data): + """ + Bit 0: Low capacity alarm: 1 warning only, 0 nomal -> OK + Bit 1: MOS tube overtemperature alarm: 1 alarm, 0 nomal -> OK + Bit 2: Charge over voltage alarm: 1 alarm, 0 nomal -> OK + Bit 3: Discharge undervoltage alarm: 1 alarm, 0 nomal -> OK + Bit 4: Battery overtemperature alarm: 1 alarm, 0 nomal -> OK + Bit 5: Charge overcurrent alarm: 1 alarm, 0 nomal -> OK + Bit 6: discharge over current alarm: 1 alarm, 0 nomal -> OK + Bit 7: core differential pressure alarm: 1 alarm, 0 nomal -> OK + Bit 8: overtemperature alarm in the battery box: 1 alarm, 0 nomal -> OK + Bit 9: Battery low temperature alarm: 1 alarm, 0 nomal -> OK + Bit 10: Unit overvoltage: 1 alarm, 0 nomal -> OK + Bit 11: Unit undervoltage: 1 alarm, 0 nomal -> OK + Bit 12:309_A protection: 1 alarm, 0 nomal + Bit 13:309_B protection: 1 alarm, 0 nomal + """ pos = 13 tmp = bin(byte_data)[15 - pos :].rjust(pos + 1, utils.zero_char) # logger.debug(tmp) + + # low capacity alarm self.protection.soc_low = 2 if is_bit_set(tmp[pos - 0]) else 0 - self.protection.set_IC_inspection = ( - 2 if is_bit_set(tmp[pos - 1]) else 0 - ) # BMS over temp + # MOSFET temperature alarm + self.protection.temp_high_internal = 2 if is_bit_set(tmp[pos - 1]) else 0 + # charge over voltage alarm self.protection.voltage_high = 2 if is_bit_set(tmp[pos - 2]) else 0 + # discharge under voltage alarm self.protection.voltage_low = 2 if is_bit_set(tmp[pos - 3]) else 0 + # charge overcurrent alarm self.protection.current_over = 1 if is_bit_set(tmp[pos - 5]) else 0 + # discharge over current alarm self.protection.current_under = 1 if is_bit_set(tmp[pos - 6]) else 0 + # core differential pressure alarm OR unit overvoltage alarm self.protection.cell_imbalance = ( 2 if is_bit_set(tmp[pos - 7]) else 1 if is_bit_set(tmp[pos - 10]) else 0 ) - self.protection.voltage_cell_low = 2 if is_bit_set(tmp[pos - 11]) else 0 - # there is just a BMS and Battery temp alarm (not high/low) - self.protection.temp_high_charge = ( + # unit undervoltage alarm + self.protection.voltage_cell_low = 1 if is_bit_set(tmp[pos - 11]) else 0 + # battery overtemperature alarm OR overtemperature alarm in the battery box + alarm_temp_high = ( 1 if is_bit_set(tmp[pos - 4]) or is_bit_set(tmp[pos - 8]) else 0 ) + # battery low temperature alarm + alarm_temp_low = 1 if is_bit_set(tmp[pos - 9]) else 0 + # check if low/high temp alarm arise during charging + self.protection.temp_high_charge = ( + 1 if self.current > 0 and alarm_temp_high == 1 else 0 + ) self.protection.temp_low_charge = ( - 1 if is_bit_set(tmp[pos - 4]) or is_bit_set(tmp[pos - 8]) else 0 + 1 if self.current > 0 and alarm_temp_low == 1 else 0 ) + # check if low/high temp alarm arise during discharging self.protection.temp_high_discharge = ( - 1 if is_bit_set(tmp[pos - 4]) or is_bit_set(tmp[pos - 8]) else 0 + 1 if self.current <= 0 and alarm_temp_high == 1 else 0 ) self.protection.temp_low_discharge = ( - 1 if is_bit_set(tmp[pos - 4]) or is_bit_set(tmp[pos - 8]) else 0 + 1 if self.current <= 0 and alarm_temp_low == 1 else 0 ) def read_serial_data_jkbms(self, command: str) -> bool: From 89dfdbf3a8418e94dc0456c82972eba5516d7e3c Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 14:49:15 +0200 Subject: [PATCH 090/209] Removed Alarms/HighCellVoltage It does not exist on the dbus --- etc/dbus-serialbattery/dbushelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 5a11deed..e7a882ff 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -252,7 +252,7 @@ def setup_vedbus(self): self._dbusservice.add_path("/Alarms/LowVoltage", None, writeable=True) self._dbusservice.add_path("/Alarms/HighVoltage", None, writeable=True) self._dbusservice.add_path("/Alarms/LowCellVoltage", None, writeable=True) - self._dbusservice.add_path("/Alarms/HighCellVoltage", None, writeable=True) + # self._dbusservice.add_path("/Alarms/HighCellVoltage", None, writeable=True) ## does not exist on the dbus self._dbusservice.add_path("/Alarms/LowSoc", None, writeable=True) self._dbusservice.add_path("/Alarms/HighChargeCurrent", None, writeable=True) self._dbusservice.add_path("/Alarms/HighDischargeCurrent", None, writeable=True) From 8ffa730d13249d7eada57a0ab32a2f555a3c8b2d Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 14:51:41 +0200 Subject: [PATCH 091/209] Changed: Moved charge mode from IO page to Paramaters page --- etc/dbus-serialbattery/dbushelper.py | 10 ++--- etc/dbus-serialbattery/installqml.sh | 7 +++ .../qml/PageBatteryParameters.qml | 44 +++++++++++++++++++ etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 6 --- etc/dbus-serialbattery/restoregui.sh | 5 +++ 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 etc/dbus-serialbattery/qml/PageBatteryParameters.qml diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index e7a882ff..ed00c75e 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -245,7 +245,7 @@ def setup_vedbus(self): self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) - self._dbusservice.add_path("/Io/ChargeMode", None, writeable=True) + self._dbusservice.add_path("/Info/ChargeMode", None, writeable=True) # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms @@ -384,7 +384,7 @@ def publish_dbus(self): else 0 ) self._dbusservice["/Io/AllowToBalance"] = 1 if self.battery.balance_fet else 0 - self._dbusservice["/Io/ChargeMode"] = self.battery.charge_mode + self._dbusservice["/Info/ChargeMode"] = self.battery.charge_mode self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None @@ -408,6 +408,9 @@ def publish_dbus(self): ] = self.battery.get_max_temp_id() self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() + # Voltage control + self._dbusservice["/Info/MaxChargeVoltage"] = self.battery.control_voltage + # Charge control self._dbusservice[ "/Info/MaxChargeCurrent" @@ -416,9 +419,6 @@ def publish_dbus(self): "/Info/MaxDischargeCurrent" ] = self.battery.control_discharge_current - # Voltage control - self._dbusservice["/Info/MaxChargeVoltage"] = self.battery.control_voltage - # Updates from cells self._dbusservice["/System/MinVoltageCellId"] = self.battery.get_min_cell_desc() self._dbusservice["/System/MaxVoltageCellId"] = self.battery.get_max_cell_desc() diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh index 5659652c..4ce93910 100644 --- a/etc/dbus-serialbattery/installqml.sh +++ b/etc/dbus-serialbattery/installqml.sh @@ -36,6 +36,10 @@ function versionStringToNumber () if [ ! -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageBattery.qml /opt/victronenergy/gui/qml/PageBattery.qml.backup fi +# backup old PageBatteryParameters.qml once. New firmware upgrade will remove the backup +if [ ! -f /opt/victronenergy/gui/qml/PageBatteryParameters.qml.backup ]; then + cp /opt/victronenergy/gui/qml/PageBatteryParameters.qml /opt/victronenergy/gui/qml/PageBatteryParameters.qml.backup +fi # backup old PageLynxIonIo.qml once. New firmware upgrade will remove the backup if [ ! -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then cp /opt/victronenergy/gui/qml/PageLynxIonIo.qml /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup @@ -44,6 +48,8 @@ fi cp /data/etc/dbus-serialbattery/qml/PageBattery.qml /opt/victronenergy/gui/qml/ # copy new PageBatteryCellVoltages cp /data/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml /opt/victronenergy/gui/qml/ +# copy new PageBatteryParameters.qml +cp /data/etc/dbus-serialbattery/qml/PageBatteryParameters.qml /opt/victronenergy/gui/qml/ # copy new PageBatterySetup cp /data/etc/dbus-serialbattery/qml/PageBatterySetup.qml /opt/victronenergy/gui/qml/ # copy new PageLynxIonIo.qml @@ -64,6 +70,7 @@ if (( $venusVersionNumber < $versionNumber )); then echo -n "Venus OS $(head -n 1 /opt/victronenergy/version) is older than v3.00~14. Replacing VisibleItemModel with VisualItemModel... " fileList="$qmlDir/PageBattery.qml" fileList+=" $qmlDir/PageBatteryCellVoltages.qml" + fileList+=" $qmlDir/PageBatteryParameters.qml" fileList+=" $qmlDir/PageBatterySetup.qml" fileList+=" $qmlDir/PageLynxIonIo.qml" for file in $fileList ; do diff --git a/etc/dbus-serialbattery/qml/PageBatteryParameters.qml b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml new file mode 100644 index 00000000..3dac2945 --- /dev/null +++ b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml @@ -0,0 +1,44 @@ +import QtQuick 1.1 +import com.victron.velib 1.0 + +MbPage { + id: root + + property variant service + + model: VisibleItemModel { + + MbItemValue { + description: qsTr("Charge mode") + item.bind: service.path("/Info/ChargeMode") + show: item.valid + } + + MbItemValue { + description: qsTr("Charge Voltage Limit (CVL)") + item.bind: service.path("/Info/MaxChargeVoltage") + } + + MbItemValue { + description: qsTr("Charge limitation") + item.bind: service.path("/Info/ChargeLimitation") + show: item.valid + } + + MbItemValue { + description: qsTr("Charge Current Limit (CCL)") + item.bind: service.path("/Info/MaxChargeCurrent") + } + + MbItemValue { + description: qsTr("Discharge Current Limit (DCL)") + item.bind: service.path("/Info/MaxDischargeCurrent") + } + + MbItemValue { + description: qsTr("Low Voltage Disconnect (always ignored)") + item.bind: service.path("/Info/BatteryLowVoltage") + showAccessLevel: User.AccessService + } + } +} diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index a6eb59c3..e6ad7106 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -18,12 +18,6 @@ MbPage { ] } - MbItemValue { - description: qsTr("Charge mode") - item.bind: service.path("/Io/ChargeMode") - show: item.valid - } - MbItemOptions { description: qsTr("Allow to charge") bind: Utils.path(bindPrefix, "/Io/AllowToCharge") diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restoregui.sh index 7a59c9c9..861f3410 100644 --- a/etc/dbus-serialbattery/restoregui.sh +++ b/etc/dbus-serialbattery/restoregui.sh @@ -9,6 +9,11 @@ if [ -f /opt/victronenergy/gui/qml/PageBattery.qml.backup ]; then echo "PageBattery.qml was restored." fi # restore original backup +if [ -f /opt/victronenergy/gui/qml/PageBatteryParameters.qml.backup ]; then + cp -f /opt/victronenergy/gui/qml/PageBatteryParameters.qml.backup /opt/victronenergy/gui/qml/PageBatteryParameters.qml + echo "PageBatteryParameters.qml was restored." +fi +# restore original backup if [ -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup ]; then cp -f /opt/victronenergy/gui/qml/PageLynxIonIo.qml.backup /opt/victronenergy/gui/qml/PageLynxIonIo.qml echo "PageLynxIonIo.qml was restored." From 1940d5dd8b2a20750da6b1c2f55453d311dd2b6e Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 16:23:37 +0200 Subject: [PATCH 092/209] Added: Show (dis)charge current limitation reason --- etc/dbus-serialbattery/battery.py | 134 +++++++++++++++--- etc/dbus-serialbattery/dbushelper.py | 14 +- .../qml/PageBatteryParameters.qml | 6 + 3 files changed, 136 insertions(+), 18 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index e28d5b5f..90bd6a93 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -84,6 +84,8 @@ def __init__(self, port, baud, address): self.control_voltage = None self.allow_max_voltage = True self.charge_mode = None + self.charge_limitation = None + self.discharge_limitation = None self.control_voltage_last_set = 0 self.max_voltage_start_time = None self.control_current = None @@ -295,15 +297,65 @@ def manage_charge_voltage_step(self) -> None: def manage_charge_current(self) -> None: # Manage Charge Current Limitations - charge_limits = [self.max_battery_charge_current] - if utils.CCCM_SOC_ENABLE: - charge_limits.append(self.calcMaxChargeCurrentReferringToSoc()) + charge_limits = [ + self.max_battery_charge_current + ] # gets removed after finished testing + charge_limits_new = {self.max_battery_charge_current: "Default"} + if utils.CCCM_CV_ENABLE: - charge_limits.append(self.calcMaxChargeCurrentReferringToCellVoltage()) + tmp = self.calcMaxChargeCurrentReferringToCellVoltage() + charge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_charge_current: " + # + str(self.max_battery_charge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_charge_current != tmp: + if tmp in charge_limits_new: + charge_limits_new.update( + {tmp: charge_limits_new[tmp] + ", cell voltage"} + ) + else: + charge_limits_new.update({tmp: "cell voltage"}) + if utils.CCCM_T_ENABLE: - charge_limits.append(self.calcMaxChargeCurrentReferringToTemperature()) + tmp = self.calcMaxChargeCurrentReferringToTemperature() + charge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_charge_current: " + # + str(self.max_battery_charge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_charge_current != tmp: + if tmp in charge_limits_new: + charge_limits_new.update({tmp: charge_limits_new[tmp] + ", temp"}) + else: + charge_limits_new.update({tmp: "temp"}) - self.control_charge_current = round(min(charge_limits), 3) + if utils.CCCM_SOC_ENABLE: + tmp = self.calcMaxChargeCurrentReferringToSoc() + charge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_charge_current: " + # + str(self.max_battery_charge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_charge_current != tmp: + if tmp in charge_limits_new: + charge_limits_new.update({tmp: charge_limits_new[tmp] + ", SoC"}) + else: + charge_limits_new.update({tmp: "SoC"}) + + self.control_charge_current = round( + min(charge_limits), 3 + ) # gets changed after finished testing + + self.charge_limitation = ( + charge_limits_new[min(charge_limits_new)] + + " (" + + str(round(min(charge_limits_new), 3)) + + ")" + ) if self.control_charge_current == 0: self.control_allow_charge = False @@ -311,19 +363,69 @@ def manage_charge_current(self) -> None: self.control_allow_charge = True # Manage Discharge Current Limitations - discharge_limits = [self.max_battery_discharge_current] - if utils.DCCM_SOC_ENABLE: - discharge_limits.append(self.calcMaxDischargeCurrentReferringToSoc()) + discharge_limits = [ + self.max_battery_discharge_current + ] # gets removed after finished testing + discharge_limits_new = {self.max_battery_discharge_current: "Default"} + if utils.DCCM_CV_ENABLE: - discharge_limits.append( - self.calcMaxDischargeCurrentReferringToCellVoltage() - ) + tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() + discharge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_discharge_current: " + # + str(self.max_battery_discharge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_discharge_current != tmp: + if tmp in discharge_limits_new: + discharge_limits_new.update( + {tmp: discharge_limits_new[tmp] + ", cell voltage"} + ) + else: + discharge_limits_new.update({tmp: "cell voltage"}) + if utils.DCCM_T_ENABLE: - discharge_limits.append( - self.calcMaxDischargeCurrentReferringToTemperature() - ) + tmp = self.calcMaxDischargeCurrentReferringToTemperature() + discharge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_discharge_current: " + # + str(self.max_battery_discharge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_discharge_current != tmp: + if tmp in discharge_limits_new: + discharge_limits_new.update( + {tmp: discharge_limits_new[tmp] + ", temp"} + ) + else: + discharge_limits_new.update({tmp: "temp"}) - self.control_discharge_current = round(min(discharge_limits), 3) + if utils.DCCM_SOC_ENABLE: + tmp = self.calcMaxDischargeCurrentReferringToSoc() + discharge_limits.append(tmp) # gets removed after finished testing + + # logging.error("self.max_battery_discharge_current: " + # + str(self.max_battery_discharge_current) + # + " - tmp: " + # + str(tmp)) + if self.max_battery_discharge_current != tmp: + if tmp in discharge_limits_new: + discharge_limits_new.update( + {tmp: discharge_limits_new[tmp] + ", SoC"} + ) + else: + discharge_limits_new.update({tmp: "SoC"}) + + self.control_discharge_current = round( + min(discharge_limits), 3 + ) # gets changed after finished testing + + self.discharge_limitation = ( + discharge_limits_new[min(discharge_limits_new)] + + " (" + + str(round(min(discharge_limits_new), 3)) + + ")" + ) if self.control_discharge_current == 0: self.control_allow_discharge = False diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index ed00c75e..36b3c819 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -155,6 +155,11 @@ def setup_vedbus(self): writeable=True, gettextcallback=lambda p, v: "{:0.2f}A".format(v), ) + + self._dbusservice.add_path("/Info/ChargeMode", None, writeable=True) + self._dbusservice.add_path("/Info/ChargeLimitation", None, writeable=True) + self._dbusservice.add_path("/Info/DischargeLimitation", None, writeable=True) + self._dbusservice.add_path( "/System/NrOfCellsPerBattery", self.battery.cell_count, writeable=True ) @@ -245,7 +250,6 @@ def setup_vedbus(self): self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) - self._dbusservice.add_path("/Info/ChargeMode", None, writeable=True) # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms @@ -384,7 +388,6 @@ def publish_dbus(self): else 0 ) self._dbusservice["/Io/AllowToBalance"] = 1 if self.battery.balance_fet else 0 - self._dbusservice["/Info/ChargeMode"] = self.battery.charge_mode self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 if self.battery.charge_fet is None @@ -419,6 +422,13 @@ def publish_dbus(self): "/Info/MaxDischargeCurrent" ] = self.battery.control_discharge_current + # Voltage and charge control info + self._dbusservice["/Info/ChargeMode"] = self.battery.charge_mode + self._dbusservice["/Info/ChargeLimitation"] = self.battery.charge_limitation + self._dbusservice[ + "/Info/DischargeLimitation" + ] = self.battery.discharge_limitation + # Updates from cells self._dbusservice["/System/MinVoltageCellId"] = self.battery.get_min_cell_desc() self._dbusservice["/System/MaxVoltageCellId"] = self.battery.get_max_cell_desc() diff --git a/etc/dbus-serialbattery/qml/PageBatteryParameters.qml b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml index 3dac2945..beff5369 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryParameters.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml @@ -30,6 +30,12 @@ MbPage { item.bind: service.path("/Info/MaxChargeCurrent") } + MbItemValue { + description: qsTr("Discharge limitation") + item.bind: service.path("/Info/DischargeLimitation") + show: item.valid + } + MbItemValue { description: qsTr("Discharge Current Limit (DCL)") item.bind: service.path("/Info/MaxDischargeCurrent") From acd9ea31778b990fe7a0fb0d6ffd0609baddbab6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 16:24:39 +0200 Subject: [PATCH 093/209] added changelog --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ae9f1cc5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +## v1.0.0-jkbms_ble + +* Added: Allow user to enable/disable Bluetooth over the config file. +* Added: Balancing status for JKBMS +* Added: Balancing switch status for JKBMS +* Added: Balancing switch status to the GUI -> SerialBattery -> IO +* Added: Charge Mode display +* Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) +* Added: Create empty `config.ini` for easier user usage +* Added: Cronjob to restart Bluetooth service every 12 hours +* Added: Driver uninstall script +* Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 +* Added: HighInternalTemperature alarm (MOSFET) for JKBMS +* Added: Install needed components automatically after a Venus OS upgrade +* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 +* Added: JKBMS BLE - Balancing switch status +* Added: JKBMS BLE - Capacity +* Added: JKBMS BLE - Cell imbalance alert +* Added: JKBMS BLE - Charging switch status +* Added: JKBMS BLE - Discharging switch status +* Added: JKBMS BLE - MOS temperature +* Added: JKBMS BLE - Show if balancing is active and which cells are balancing +* Added: Post install notes +* Added: Script to install directly from repository +* Added: Show charge mode (absorption, bulk, ...) in IO page +* Added: Show charge/discharge limitation reason +* Added: Show TimeToSoc in GUI only, if enabled +* Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) +* Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository +* Changed: Added QML to `restoregui.sh` +* Changed: Bash output +* Changed: Default config file + * Added missing descriptions to make it much clearer to understand + * Changed name from `default_config.ini` to `config.default.ini` https://github.com/Louisvdw/dbus-serialbattery/pull/412#issuecomment-1434287942 + * Changed TimeToSoc default value `TIME_TO_SOC_VALUE_TYPE` from `Both seconds and time string " [d h m s]"` to `1 Seconds` + * Changed TimeToSoc description + * Changed value positions, added groups and much clearer descriptions +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 +* Changed: Fixed black lint errors +* Changed: Fixed cell balancing background for cells 17-24 +* Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 +* Changed: Logging to get relevant data +* Changed: Moved ble part to `installble.sh` +* Changed: Optimized installation scripts +* Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 +* Changed: Separate Time-To-Go and Time-To-SoC activation +* Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS +* Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` +* Changed: Time-To-Soc string from `days, HR:MN:SC` to `d h m s` (same as Time-To-Go) +* Changed: Uninstall also installed Bluetooth modules on uninstall. From 2e67a5ecde173a4b80a25f6ed4eb63aafd4334cc Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 17:58:06 +0200 Subject: [PATCH 094/209] corrected file permissions --- etc/dbus-serialbattery/disabledriver.sh | 0 etc/dbus-serialbattery/install-nightly.sh | 0 etc/dbus-serialbattery/installble.sh | 1 + etc/dbus-serialbattery/installlocal.sh | 0 etc/dbus-serialbattery/installqml.sh | 0 etc/dbus-serialbattery/installrelease.sh | 0 etc/dbus-serialbattery/reinstalllocal.sh | 0 etc/dbus-serialbattery/restart-driver-and-ble.sh | 0 etc/dbus-serialbattery/restoregui.sh | 0 etc/dbus-serialbattery/service/log/run | 0 etc/dbus-serialbattery/service/run | 0 etc/dbus-serialbattery/start-serialbattery.sh | 0 etc/dbus-serialbattery/uninstall.sh | 0 13 files changed, 1 insertion(+) mode change 100644 => 100755 etc/dbus-serialbattery/disabledriver.sh mode change 100644 => 100755 etc/dbus-serialbattery/install-nightly.sh mode change 100644 => 100755 etc/dbus-serialbattery/installlocal.sh mode change 100644 => 100755 etc/dbus-serialbattery/installqml.sh mode change 100644 => 100755 etc/dbus-serialbattery/installrelease.sh mode change 100644 => 100755 etc/dbus-serialbattery/reinstalllocal.sh mode change 100644 => 100755 etc/dbus-serialbattery/restart-driver-and-ble.sh mode change 100644 => 100755 etc/dbus-serialbattery/restoregui.sh mode change 100644 => 100755 etc/dbus-serialbattery/service/log/run mode change 100644 => 100755 etc/dbus-serialbattery/service/run mode change 100644 => 100755 etc/dbus-serialbattery/start-serialbattery.sh mode change 100644 => 100755 etc/dbus-serialbattery/uninstall.sh diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index abe86010..669df03e 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -38,5 +38,6 @@ install_service() { ## Uncomment for each adapter here, increase the number for each adapter/service +install_service 0 Jkbms_Ble C8:47:8C:E8:12:04 # install_service 0 Jkbms_Ble C8:47:8C:12:34:56 # install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/installqml.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/installrelease.sh b/etc/dbus-serialbattery/installrelease.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/restart-driver-and-ble.sh b/etc/dbus-serialbattery/restart-driver-and-ble.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restoregui.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/service/log/run b/etc/dbus-serialbattery/service/log/run old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/service/run b/etc/dbus-serialbattery/service/run old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/start-serialbattery.sh b/etc/dbus-serialbattery/start-serialbattery.sh old mode 100644 new mode 100755 diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh old mode 100644 new mode 100755 From 3f81fa52434ad3d3c88d0065873d01928792797f Mon Sep 17 00:00:00 2001 From: seidler2547 Date: Wed, 26 Apr 2023 15:59:33 +0000 Subject: [PATCH 095/209] fix dbus-daemon memory leak `poll_battery` is already called from the dbus mainloop in a separate thread. Opening a new thread here (and especially never closing it) will accumulate threads and leads to problems like `dbus-daemon` memory usage increasing over time, leading to eventual reboot --- etc/dbus-serialbattery/dbus-serialbattery.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 824a44cc..949ec5ac 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -57,11 +57,7 @@ def main(): def poll_battery(loop): - # Run in separate thread. Pass in the mainloop so the thread can kill us if there is an exception. - poller = Thread(target=lambda: helper.publish_battery(loop)) - # Thread will die with us if deamon - poller.daemon = True - poller.start() + helper.publish_battery(loop) return True def get_battery(_port) -> Union[Battery, None]: From 3caad7354662bc6dc5c255926bbd42bb68dff022 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 18:00:01 +0200 Subject: [PATCH 096/209] fix typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae9f1cc5..a2a0209f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ * Added: JKBMS BLE - Show if balancing is active and which cells are balancing * Added: Post install notes * Added: Script to install directly from repository -* Added: Show charge mode (absorption, bulk, ...) in IO page +* Added: Show charge mode (absorption, bulk, ...) in Parameters page * Added: Show charge/discharge limitation reason * Added: Show TimeToSoc in GUI only, if enabled * Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) From e28facfa992bc6357267ac99a753e777ca24b75d Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 19:50:18 +0200 Subject: [PATCH 097/209] Update readme --- README.md | 39 +++++++++++++++------------- etc/dbus-serialbattery/installble.sh | 1 - 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 36bd7163..243e7365 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,39 @@ # dbus-serialbattery -This is a driver for VenusOS devices (any GX device sold by Victron or a Raspberry Pi running the VenusOS image). +This is a driver for VenusOS devices (any GX device sold by Victron or a Raspberry Pi running the VenusOS image). -The driver will communicate with a Battery Management System (BMS) that support serial communication (RS232, RS485 or TTL UART) and publish this data to the VenusOS system. The main purpose is to act as a Battery Monitor in your GX and supply State Of Charge (SOC) and other values to the inverter. +The driver will communicate with a Battery Management System (BMS) that support serial communication (RS232, RS485 or TTL UART) and publish this data to the VenusOS system. The main purpose is to act as a Battery Monitor in your GX and supply State of Charge (SoC) and other values to the inverter. - * [BMS Types supported](https://github.com/Louisvdw/dbus-serialbattery/wiki/BMS-types-supported) - * [FAQ](https://github.com/Louisvdw/dbus-serialbattery/wiki/FAQ) - * [Features](https://github.com/Louisvdw/dbus-serialbattery/wiki/Features) - * [How to install](https://github.com/Louisvdw/dbus-serialbattery/wiki/How-to-install) - * [Troubleshoot](https://github.com/Louisvdw/dbus-serialbattery/wiki/Troubleshoot) +## Documentation +Check the documenation for more informations. +* [Introduction](https://louisvdw.github.io/dbus-serialbattery/) + * [Features](https://louisvdw.github.io/dbus-serialbattery/general/features) + * [Supported BMS](https://louisvdw.github.io/dbus-serialbattery/general/supported-bms) + * [How to install](https://louisvdw.github.io/dbus-serialbattery/general/install) +* [Troubleshoot](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/) + * [FAQ (Frequently Asked Questions)](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/faq) -### Supporting this project: +## Supporting this project: If you find this driver helpful please considder supporting this project. You can buy me a Ko-Fi or get in contact if you would like to donate hardware. [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z73LCW1) or using [Paypal.me](https://paypal.me/innernet) -### Developer Remarks -To develop this project, install the requirements. This project makes use of velib_python which is pre-installed on -Venus-OS Devices under `/opt/victronenergy/dbus-systemcalc-py/ext/velib_python`. To use the python files locally, -`git clone` the [velib_python](https://github.com/victronenergy/velib_python) project to velib_python and add +## Developer Remarks +To develop this project, install the requirements. This project makes use of velib_python which is pre-installed on +Venus OS Devices under `/opt/victronenergy/dbus-systemcalc-py/ext/velib_python`. To use the python files locally, +`git clone` the [velib_python](https://github.com/victronenergy/velib_python) project to velib_python and add velib_python to the `PYTHONPATH` environment variable. -#### How it works -* Each supported BMS needs to implement the abstract base class `Battery` from `battery.py`. -* `dbus-serialbattery.py` tries to figure out the correct connected BMS by looping through all known implementations of +## How it works +* Each supported BMS needs to implement the abstract base class `Battery` from `battery.py`. +* `dbus-serialbattery.py` tries to figure out the correct connected BMS by looping through all known implementations of `Battery` and executing its `test_connection()`. If this returns true, `dbus-serialbattery.py` sticks with this battery -and then periodically executes `dbushelpert.publish_battery()`. `publish_battery()` executes `Battery.refresh_data()` which +and then periodically executes `dbushelper.publish_battery()`. `publish_battery()` executes `Battery.refresh_data()` which updates the fields of Battery. It then publishes those fields to dbus using `dbushelper.publish_dbus()` * The Victron Device will be "controlled" by the values published on `/Info/` - namely: * `/Info/MaxChargeCurrent ` * `/Info/MaxDischargeCurrent` * `/Info/MaxChargeVoltage` - * `/Info/BatteryLowVoltage` + * `/Info/BatteryLowVoltage` (note that Low Voltage is ignored by the system) * `/Info/ChargeRequest` (not implemented in dbus-serialbattery) -For more details on the victron dbus interface see [the official victron dbus documentation](https://github.com/victronenergy/venus/wiki/dbus) +For more details on the Victron dbus interface see [the official Victron dbus documentation](https://github.com/victronenergy/venus/wiki/dbus). diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 669df03e..abe86010 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -38,6 +38,5 @@ install_service() { ## Uncomment for each adapter here, increase the number for each adapter/service -install_service 0 Jkbms_Ble C8:47:8C:E8:12:04 # install_service 0 Jkbms_Ble C8:47:8C:12:34:56 # install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC From b011e0ee308f2ce993886557e8c36e2bdd127c76 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 20:34:09 +0200 Subject: [PATCH 098/209] Remove bluetooth option from the config file Bluetooth classes are now imported automatically, if it's a Bluetooth port --- CHANGELOG.md | 1 - etc/dbus-serialbattery/config.default.ini | 6 ------ etc/dbus-serialbattery/dbus-serialbattery.py | 11 +++++++---- etc/dbus-serialbattery/utils.py | 3 --- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2a0209f..37a5b551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## v1.0.0-jkbms_ble -* Added: Allow user to enable/disable Bluetooth over the config file. * Added: Balancing status for JKBMS * Added: Balancing switch status for JKBMS * Added: Balancing switch status to the GUI -> SerialBattery -> IO diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 91e823a5..b666c7d3 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -1,8 +1,5 @@ [DEFAULT] -; Enables the search/support for Bluetooth BMS -BLUETOOTH_ENABLED = False - ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) @@ -20,9 +17,6 @@ MAX_BATTERY_DISCHARGE_CURRENT = 60.0 ; If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied ; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. ; Allow max voltage of 55.2V again, if SoC is once below 90% -; OR -; The battery reached max voltage of 55.2V and the max cell difference is 0.01V, then switch to float voltage of 53.6V to don't stress the batteries. -; Allow max voltage of 55.2V again if max cell difference is above 0.02V ; Charge voltage control management enable (True/False). CVCM_ENABLE = False diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 949ec5ac..fdcc05a5 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -29,10 +29,6 @@ from ecs import Ecs from lifepower import Lifepower -# import Bluetooth BMS classes -if utils.BLUETOOTH_ENABLED: - from jkbms_ble import Jkbms_Ble - supported_bms_types = [ {"bms": LltJbd, "baud": 9600}, @@ -99,6 +95,13 @@ def get_port() -> str: port = get_port() battery = None if port.endswith("_Ble") and len(sys.argv) > 2: + """ + Import ble classes only, if it's a ble port, else the driver won't start due to missing python modules + This prevent problems when using the driver only with a serial connection + """ + if port == "Jkbms_Ble": + from jkbms_ble import Jkbms_Ble + class_ = eval(port) testbms = class_("", 9600, sys.argv[2]) if testbms.test_connection() is True: diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index e1bbac75..5bd66408 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -40,9 +40,6 @@ def _get_list_from_config( zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" -# Enables the search/support for Bluetooth BMS -BLUETOOTH_ENABLED = "True" == config["DEFAULT"]["BLUETOOTH_ENABLED"] - # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps # True is a linear mode. For CCL and DCL the values between the steps are calculated for From 8d0db98f2853209c4fe1796df5b3745fdc17c27d Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 20:36:49 +0200 Subject: [PATCH 099/209] Small word case changes --- etc/dbus-serialbattery/battery.py | 20 +++++++++---------- .../qml/PageBatteryParameters.qml | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 90bd6a93..6b6531dc 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -300,7 +300,7 @@ def manage_charge_current(self) -> None: charge_limits = [ self.max_battery_charge_current ] # gets removed after finished testing - charge_limits_new = {self.max_battery_charge_current: "Default"} + charge_limits_new = {self.max_battery_charge_current: "None (Max Config Limit)"} if utils.CCCM_CV_ENABLE: tmp = self.calcMaxChargeCurrentReferringToCellVoltage() @@ -313,10 +313,10 @@ def manage_charge_current(self) -> None: if self.max_battery_charge_current != tmp: if tmp in charge_limits_new: charge_limits_new.update( - {tmp: charge_limits_new[tmp] + ", cell voltage"} + {tmp: charge_limits_new[tmp] + ", Cell voltage"} ) else: - charge_limits_new.update({tmp: "cell voltage"}) + charge_limits_new.update({tmp: "Cell voltage"}) if utils.CCCM_T_ENABLE: tmp = self.calcMaxChargeCurrentReferringToTemperature() @@ -328,9 +328,9 @@ def manage_charge_current(self) -> None: # + str(tmp)) if self.max_battery_charge_current != tmp: if tmp in charge_limits_new: - charge_limits_new.update({tmp: charge_limits_new[tmp] + ", temp"}) + charge_limits_new.update({tmp: charge_limits_new[tmp] + ", Temp"}) else: - charge_limits_new.update({tmp: "temp"}) + charge_limits_new.update({tmp: "Temp"}) if utils.CCCM_SOC_ENABLE: tmp = self.calcMaxChargeCurrentReferringToSoc() @@ -366,7 +366,7 @@ def manage_charge_current(self) -> None: discharge_limits = [ self.max_battery_discharge_current ] # gets removed after finished testing - discharge_limits_new = {self.max_battery_discharge_current: "Default"} + discharge_limits_new = {self.max_battery_discharge_current: "None (Max Config Limit)"} if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() @@ -379,10 +379,10 @@ def manage_charge_current(self) -> None: if self.max_battery_discharge_current != tmp: if tmp in discharge_limits_new: discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", cell voltage"} + {tmp: discharge_limits_new[tmp] + ", Cell voltage"} ) else: - discharge_limits_new.update({tmp: "cell voltage"}) + discharge_limits_new.update({tmp: "Cell voltage"}) if utils.DCCM_T_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToTemperature() @@ -395,10 +395,10 @@ def manage_charge_current(self) -> None: if self.max_battery_discharge_current != tmp: if tmp in discharge_limits_new: discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", temp"} + {tmp: discharge_limits_new[tmp] + ", Temp"} ) else: - discharge_limits_new.update({tmp: "temp"}) + discharge_limits_new.update({tmp: "Temp"}) if utils.DCCM_SOC_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToSoc() diff --git a/etc/dbus-serialbattery/qml/PageBatteryParameters.qml b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml index beff5369..b95161a3 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryParameters.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryParameters.qml @@ -9,7 +9,7 @@ MbPage { model: VisibleItemModel { MbItemValue { - description: qsTr("Charge mode") + description: qsTr("Charge Mode") item.bind: service.path("/Info/ChargeMode") show: item.valid } @@ -20,7 +20,7 @@ MbPage { } MbItemValue { - description: qsTr("Charge limitation") + description: qsTr("Charge Limitation") item.bind: service.path("/Info/ChargeLimitation") show: item.valid } @@ -31,7 +31,7 @@ MbPage { } MbItemValue { - description: qsTr("Discharge limitation") + description: qsTr("Discharge Limitation") item.bind: service.path("/Info/DischargeLimitation") show: item.valid } From 2ffa35869e67e0745ad015be0461d2465a7844ac Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 20:41:47 +0200 Subject: [PATCH 100/209] Added: Show specific TimeToSoC points in GUI Only if 0%, 10%, 20%, 80%, 90% and/or 100% are selected --- CHANGELOG.md | 3 +- etc/dbus-serialbattery/qml/PageBattery.qml | 36 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37a5b551..391dfecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,8 @@ * Added: Script to install directly from repository * Added: Show charge mode (absorption, bulk, ...) in Parameters page * Added: Show charge/discharge limitation reason -* Added: Show TimeToSoc in GUI only, if enabled +* Added: Show specific TimeToSoC points in GUI, if 0%, 10%, 20%, 80%, 90% and/or 100% are selected +* Added: Show TimeToGo in GUI only, if enabled * Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) * Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository * Changed: Added QML to `restoregui.sh` diff --git a/etc/dbus-serialbattery/qml/PageBattery.qml b/etc/dbus-serialbattery/qml/PageBattery.qml index 3f3b58f8..286ce74c 100644 --- a/etc/dbus-serialbattery/qml/PageBattery.qml +++ b/etc/dbus-serialbattery/qml/PageBattery.qml @@ -190,6 +190,42 @@ MbPage { show: item.seen } + MbItemValue { + description: qsTr("Time-to-SoC 0%") + item.bind: service.path("/TimeToSoC/0") + show: item.seen + } + + MbItemValue { + description: qsTr("Time-to-SoC 10%") + item.bind: service.path("/TimeToSoC/10") + show: item.seen + } + + MbItemValue { + description: qsTr("Time-to-SoC 20%") + item.bind: service.path("/TimeToSoC/20") + show: item.seen + } + + MbItemValue { + description: qsTr("Time-to-SoC 80%") + item.bind: service.path("/TimeToSoC/80") + show: item.seen + } + + MbItemValue { + description: qsTr("Time-to-SoC 90%") + item.bind: service.path("/TimeToSoC/90") + show: item.seen + } + + MbItemValue { + description: qsTr("Time-to-SoC 100%") + item.bind: service.path("/TimeToSoC/100") + show: item.seen + } + MbItemOptions { description: qsTr("Relay state") bind: service.path("/Relay/0/State") From 5a16cbdc94f4e8169f40a4839c6fad654c688493 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 26 Apr 2023 20:50:35 +0200 Subject: [PATCH 101/209] fix black lint error --- etc/dbus-serialbattery/battery.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 6b6531dc..54417720 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -366,7 +366,9 @@ def manage_charge_current(self) -> None: discharge_limits = [ self.max_battery_discharge_current ] # gets removed after finished testing - discharge_limits_new = {self.max_battery_discharge_current: "None (Max Config Limit)"} + discharge_limits_new = { + self.max_battery_discharge_current: "None (Max Config Limit)" + } if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() From 1e6d4100e1d1360b579dd0caeae93ff51f0d839a Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 14:14:46 +0200 Subject: [PATCH 102/209] Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 --- CHANGELOG.md | 1 + etc/dbus-serialbattery/lltjbd.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 391dfecb..e6243b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ * Changed: Fixed black lint errors * Changed: Fixed cell balancing background for cells 17-24 * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 +* Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 * Changed: Logging to get relevant data * Changed: Moved ble part to `installble.sh` * Changed: Optimized installation scripts diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index 2a3d536c..a8222fd6 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -138,13 +138,14 @@ def read_gen_data(self): balance2, protection, version, - self.soc, + soc, fet, self.cell_count, self.temp_sensors, ) = unpack_from(">HhHHHHhHHBBBBB", gen_data) self.voltage = voltage / 100 self.current = current / 100 + self.soc = 100 * capacity_remain / capacity self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) From 922b82c42599f5c19b7df23e5f7d56d119e9d82c Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 14:20:39 +0200 Subject: [PATCH 103/209] Fix for #397 https://github.com/Louisvdw/dbus-serialbattery/pull/484 --- CHANGELOG.md | 3 ++- etc/dbus-serialbattery/daly.py | 32 ++++++++++++++++++++------------ etc/dbus-serialbattery/utils.py | 6 ++++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6243b61..12d2ee32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,11 +40,12 @@ * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/397 by @transistorgit * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 * Changed: Fixed black lint errors * Changed: Fixed cell balancing background for cells 17-24 * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 -* Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 +* Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek * Changed: Logging to get relevant data * Changed: Moved ble part to `installble.sh` * Changed: Optimized installation scripts diff --git a/etc/dbus-serialbattery/daly.py b/etc/dbus-serialbattery/daly.py index ae96c723..492b0500 100644 --- a/etc/dbus-serialbattery/daly.py +++ b/etc/dbus-serialbattery/daly.py @@ -20,7 +20,7 @@ def __init__(self, port, baud, address): # command bytes [StartFlag=A5][Address=40][Command=94][DataLength=8][8x zero bytes][checksum] command_base = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x81" - cellvolt_buffer = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x82\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + cellvolt_buffer = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x82" command_soc = b"\x90" command_minmax_cell_volts = b"\x91" command_minmax_temp = b"\x92" @@ -31,7 +31,7 @@ def __init__(self, port, baud, address): command_cell_balance = b"\x97" command_alarm = b"\x98" BATTERYTYPE = "Daly" - LENGTH_CHECK = 4 + LENGTH_CHECK = 1 LENGTH_POS = 3 CURRENT_ZERO_CONSTANT = 30000 TEMP_ZERO_CONSTANT = 40 @@ -246,13 +246,16 @@ def read_cells_volts(self, ser): buffer[1] = self.command_address[0] # Always serial 40 or 80 buffer[2] = self.command_cell_volts[0] - maxFrame = int(self.cell_count / 3) + 1 + if (int(self.cell_count) % 3) == 0: + maxFrame = (int(self.cell_count / 3)) + else: + maxFrame = (int(self.cell_count / 3) + 1) lenFixed = ( - maxFrame * 12 - ) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + maxFrame * 13 + ) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + chksum cells_volts_data = read_serialport_data( - ser, buffer, self.LENGTH_POS, self.LENGTH_CHECK, lenFixed + ser, buffer, self.LENGTH_POS, 0, lenFixed ) if cells_volts_data is False: logger.warning("read_cells_volts") @@ -269,14 +272,19 @@ def read_cells_volts(self, ser): for idx in range(self.cell_count): self.cells.append(Cell(True)) + # logger.warning("data " + bytes(cells_volts_data).hex()) + while ( bufIdx < len(cells_volts_data) - 4 ): # we at least need 4 bytes to extract the identifiers b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: - frame, frameCell[0], frameCell[1], frameCell[2] = unpack_from( - ">Bhhh", cells_volts_data, bufIdx + 4 + frame, frameCell[0], frameCell[1], frameCell[2], _, chk = unpack_from( + ">BhhhBB", cells_volts_data, bufIdx + 4 ) + if sum(cells_volts_data[bufIdx:bufIdx+12]) & 0xFF != chk: + logger.warning("bad cell voltages checksum") + return False for idx in range(3): cellnum = ( (frame - 1) * 3 @@ -287,9 +295,9 @@ def read_cells_volts(self, ser): self.cells[cellnum].voltage = ( None if cellVoltage < lowMin else cellVoltage ) - bufIdx += 10 # BBBBBhhh -> 11 byte - bufIdx += 1 - + bufIdx += 13 # BBBBBhhhBB -> 13 byte + else: + logger.warning("bad cell voltages header") return True def read_cell_voltage_range_data(self, ser): @@ -359,7 +367,7 @@ def read_serial_data_daly(self, ser, command): start, flag, command_ret, length = unpack_from("BBBB", data) checksum = sum(data[:-1]) & 0xFF - if start == 165 and length == 8 and checksum == data[12]: + if start == 165 and length == 8 and len(data)>12 and checksum == data[12]: return data[4 : length + 4] else: logger.error(">>> ERROR: Incorrect Reply") diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 5bd66408..7cf6eac6 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -441,8 +441,10 @@ def read_serialport_data( count = 0 data = bytearray(res) - while len(data) <= length + length_check: - res = ser.read(length + length_check) + + packetlen = length_fixed if length_fixed is not None else length_pos + length_byte_size + length + length_check + while len(data) < packetlen: + res = ser.read(packetlen - len(data)) data.extend(res) # logger.info('serial data length ' + str(len(data))) sleep(0.005) From 23356f6fbe4d6668e685a49e989b71c97264e304 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 14:20:57 +0200 Subject: [PATCH 104/209] small fixes --- .github/workflows/release.yml | 1 + etc/dbus-serialbattery/battery.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b6a57da..a9ad92fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: --exclude restartservice.sh \ --exclude revov.py \ --exclude test_max17853.py \ + --exclude util_max17853.py \ etc/dbus-serialbattery/ - name: Release diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 54417720..1578a8c9 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -828,8 +828,8 @@ def log_settings(self) -> None: ) logger.info(f"> LINEAR LIMITATION ENABLE: {utils.LINEAR_LIMITATION_ENABLE}") logger.info( - f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}V | " - + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}V" + f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}A | " + + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}A" ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( From 4bd257f157d889378cedaa4e654f2fb7102edff9 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 14:53:15 +0200 Subject: [PATCH 105/209] sort bms imports --- etc/dbus-serialbattery/dbus-serialbattery.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index fdcc05a5..bb0df250 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -19,28 +19,29 @@ from utils import logger import utils from battery import Battery -from lltjbd import LltJbd + +# import battery classes +from ant import Ant from daly import Daly +from ecs import Ecs from ant import Ant from jkbms import Jkbms - -# from sinowealth import Sinowealth -from renogy import Renogy -from ecs import Ecs from lifepower import Lifepower - +from lltjbd import LltJbd +from renogy import Renogy +# from sinowealth import Sinowealth supported_bms_types = [ - {"bms": LltJbd, "baud": 9600}, {"bms": Ant, "baud": 19200}, {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, + {"bms": Ecs, "baud": 19200}, {"bms": Jkbms, "baud": 115200}, - # {"bms" : Sinowealth}, {"bms": Lifepower, "baud": 9600}, + {"bms": LltJbd, "baud": 9600}, {"bms": Renogy, "baud": 9600, "address": b"\x30"}, {"bms": Renogy, "baud": 9600, "address": b"\xF7"}, - {"bms": Ecs, "baud": 19200}, + # {"bms": Sinowealth}, ] expected_bms_types = [ battery_type From c56a9d107946313b4509c1f0c60d919a32c05636 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 14:56:15 +0200 Subject: [PATCH 106/209] Add support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 --- etc/dbus-serialbattery/dbus-serialbattery.py | 3 +- etc/dbus-serialbattery/hlpdatabms4s.py | 240 ++++ .../hlpdatabms4s_miniterm.py | 1189 +++++++++++++++++ 3 files changed, 1431 insertions(+), 1 deletion(-) create mode 100644 etc/dbus-serialbattery/hlpdatabms4s.py create mode 100644 etc/dbus-serialbattery/hlpdatabms4s_miniterm.py diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index bb0df250..b1a034bd 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -24,7 +24,7 @@ from ant import Ant from daly import Daly from ecs import Ecs -from ant import Ant +from hlpdatabms4s import HLPdataBMS4S from jkbms import Jkbms from lifepower import Lifepower from lltjbd import LltJbd @@ -36,6 +36,7 @@ {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Ecs, "baud": 19200}, + {'bms': HLPdataBMS4S, "baud": 9600}, {"bms": Jkbms, "baud": 115200}, {"bms": Lifepower, "baud": 9600}, {"bms": LltJbd, "baud": 9600}, diff --git a/etc/dbus-serialbattery/hlpdatabms4s.py b/etc/dbus-serialbattery/hlpdatabms4s.py new file mode 100644 index 00000000..63995357 --- /dev/null +++ b/etc/dbus-serialbattery/hlpdatabms4s.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +from battery import Protection, Battery, Cell +from utils import * +from struct import * +import serial +from time import sleep + + +class HLPdataBMS4S(Battery): + def __init__(self, port, baud): + super(HLPdataBMS4S, self).__init__(port, baud) + self.type = self.BATTERYTYPE + + BATTERYTYPE = "HLPdataBMS4S" + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + result = False + try: + result = self.read_test_data() + except Exception as e: + # logger.error(e, exc_info=True) + pass + return result + + def get_settings(self): + # After successful connection get_settings will be call to set up the battery. + # Set the current limits, populate cell count, etc + # Return True if success, False for failure + result = False + try: + result = self.read_settings_data() + except Exception as e: + # logger.error(e, exc_info=True) + pass + return result + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (1 second) + # Return True if success, False for failure + result = False + try: + result = self.read_status_data() + except Exception as e: + # logger.error(e, exc_info=True) + pass + return result + + # def log_settings(self): + # logger.info(f'Battery {self.type} connected to dbus from {self.port}') + # logger.info(f'=== Settings ===') + # cell_counter = len(self.cells) + # logger.info(f'> Connection voltage {self.voltage}V | current {self.current}A | SOC {self.soc}%') + # logger.info(f'> Cell count {self.cell_count} | cells populated {cell_counter}') + # logger.info(f'> CCCM SOC {CCCM_SOC_ENABLE} | DCCM SOC {DCCM_SOC_ENABLE}') + # logger.info(f'> CCCM CV {CCCM_CV_ENABLE} | DCCM CV {DCCM_CV_ENABLE}') + # logger.info(f'> CCCM T {CCCM_T_ENABLE} | DCCM T {DCCM_T_ENABLE}') + # logger.info(f'> MIN_CELL_VOLTAGE {MIN_CELL_VOLTAGE}V | MAX_CELL_VOLTAGE {MAX_CELL_VOLTAGE}V') + + return + + def read_test_data(self): + test_data = self.read_serial_data_HLPdataBMS4S(b"pv\n", 1, 15) + if test_data is False: + return False + s1 = str(test_data) + ix = s1.find("BMS4S") + if ix > 0: + self.hardware_version = s1[ix : len(s1) - 1] + self.version = self.hardware_version + self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.poll_interval = 10000 + self.control_discharge_current = 1000 + self.control_charge_current = 1000 + self.soc = 50 + self.voltage = 13.2 + self.current = 0 + self.min_battery_voltage = 12.0 + self.max_battery_voltage = 14.4 + + if self.cell_count is None: + self.cell_count = 4 + for c in range(self.cell_count): + self.cells.append(Cell(False)) + return True + return False + + def read_settings_data(self): + test_data = self.read_serial_data_HLPdataBMS4S(b"ps\n", 3, 700) + if test_data is False: + return False + s = str(test_data) + s = s.replace(",", ".") + par = get_par("BatterySize= ", s) + if par is False: + return False + self.capacity = int(par) + v = get_par("VoltHigh= ", s) + if v is False: + return False + self.max_battery_voltage = float(v) * float(4) + v = get_par("VoltLow= ", s) + if v is False: + return False + self.min_battery_voltage = float(v) * float(4) + + return True + + def read_status_data(self): + status_data = self.read_serial_data_HLPdataBMS4S(b"m1\n", 0.2, 40) + if status_data is False: + return False + par1 = str(status_data) + par = par1.split(",") + if len(par) < 8: + return False + if len(par[0]) < 7: + return False + p0 = str(par[0]) + ix = p0.find(".") + par0 = p0[ix - 1 : len(p0)] + + # v1,v2,v3,v4,current,soc,chargeoff,loadoff,vbat2,socnow,adj,beep,led,temp1,temp2... + # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14... + + self.voltage = float(par0) + float(par[1]) + float(par[2]) + float(par[3]) + self.cells[0].voltage = float(par0) + self.cells[1].voltage = float(par[1]) + self.cells[2].voltage = float(par[2]) + self.cells[3].voltage = float(par[3]) + self.current = float(par[4]) + self.soc = int(par[5]) + self.control_allow_charge = par[6] + self.charge_fet = par[6] + self.control_allow_discharge = par[7] + self.discharge_fet = par[7] + + beep = int(par[11]) + if beep == 2: + self.protection.temp_low_charge = 1 + else: + self.protection.temp_low_charge = 0 + if beep == 3: + self.protection.temp_high_charge = 1 + else: + self.protection.temp_high_charge = 0 + if beep == 4: + self.protection.voltage_low = 2 + else: + self.protection.voltage_low = 0 + if beep == 5: + self.protection.voltage_high = 2 + else: + self.protection.voltage_high = 0 + + if len(par) > 13: + nb = 0 + min = int(1000) + max = int(-1000) + ix = 13 + while ix < len(par): + tmp = par[ix].split(" ") + ix += 1 + if len(tmp) == 2: + name = tmp[0] + temp = int("".join(filter(str.isdigit, tmp[1]))) + if name[0] == "b": + nb += 1 + if temp > max: + max = temp + if temp < min: + min = temp + if nb == 1: + self.temp1 = max + if nb > 1: + self.temp1 = max + self.temp2 = min + + return True + + def manage_charge_voltage(self): + self.allow_max_voltage = True + self.control_voltage = self.max_battery_voltage + + def manage_charge_current(self): + self.control_charge_current = 1000 + self.control_discharge_current = 1000 + + def read_serial_data_HLPdataBMS4S(self, command, time, min_len): + data = read_serial_data2(command, self.port, self.baud_rate, time, min_len) + if data is False: + return False + return data + + +def read_serial_data2(command, port, baud, time, min_len): + try: + with serial.Serial(port, baudrate=baud, timeout=0.5) as ser: + ret = read_serialport_data2(ser, command, time, min_len) + if not ret is False: + return ret + return False + + except serial.SerialException as e: + logger.error(e) + return False + + +def read_serialport_data2(ser, command, time, min_len): + try: + cnt = 0 + while cnt < 3: + cnt += 1 + ser.flushOutput() + ser.flushInput() + ser.write(command) + sleep(time) + res = ser.read(1000) + if len(res) >= min_len: + return res + return False + + except serial.SerialException as e: + logger.error(e) + return False + + +def get_par(p, s): + ix = s.find(p) + if ix > 0: + ix += len(p) + for i in range(ix, len(s)): + if s[i] == " " or s[i] == 10 or s[i] == 13: + ret = s[ix:i] + return ret + return False diff --git a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py b/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py new file mode 100644 index 00000000..741ac75a --- /dev/null +++ b/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py @@ -0,0 +1,1189 @@ +#!/usr/bin/env python +# +# Very simple serial terminal +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C)2002-2020 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Modified to enable managing HLPdataBMS4S in Venus OS + +from __future__ import absolute_import + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec +from time import sleep + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == "hexlify" else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return "Ctrl+{:c}".format(ord("@") + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self, miniterm): + self.miniterm = miniterm + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == "nt": # noqa + import msvcrt + import ctypes + import platform + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + fncodes = { + ";": "\x1bOP", # F1 + "<": "\x1bOQ", # F2 + "=": "\x1bOR", # F3 + ">": "\x1bOS", # F4 + "?": "\x1b[15~", # F5 + "@": "\x1b[17~", # F6 + "A": "\x1b[18~", # F7 + "B": "\x1b[19~", # F8 + "C": "\x1b[20~", # F9 + "D": "\x1b[21~", # F10 + } + navcodes = { + "H": "\x1b[A", # UP + "P": "\x1b[B", # DOWN + "K": "\x1b[D", # LEFT + "M": "\x1b[C", # RIGHT + "G": "\x1b[H", # HOME + "O": "\x1b[F", # END + "R": "\x1b[2~", # INSERT + "S": "\x1b[3~", # DELETE + "I": "\x1b[5~", # PAGE UP + "Q": "\x1b[6~", # PAGE DOWN + } + + def __init__(self, miniterm): + super(Console, self).__init__(miniterm) + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + # ANSI handling available through SetConsoleMode since Windows 10 v1511 + # https://en.wikipedia.org/wiki/ANSI_escape_code#cite_note-win10th2-1 + if ( + platform.release() == "10" + and int(platform.version().split(".")[2]) > 10586 + ): + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + import ctypes.wintypes as wintypes + + if not hasattr(wintypes, "LPDWORD"): # PY2 + wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD) + SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode + GetConsoleMode = ctypes.windll.kernel32.GetConsoleMode + GetStdHandle = ctypes.windll.kernel32.GetStdHandle + mode = wintypes.DWORD() + GetConsoleMode(GetStdHandle(-11), ctypes.byref(mode)) + if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: + SetConsoleMode( + GetStdHandle(-11), + mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + self._saved_cm = mode + self.output = codecs.getwriter("UTF-8")(Out(sys.stdout.fileno()), "replace") + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter("UTF-8")(Out(sys.stderr.fileno()), "replace") + sys.stdout = self.output + self.output.encoding = "UTF-8" # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + try: + ctypes.windll.kernel32.SetConsoleMode( + ctypes.windll.kernel32.GetStdHandle(-11), self._saved_cm + ) + except AttributeError: # in case no _saved_cm + pass + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z is unichr(0) or z is unichr(0xE0): + try: + code = msvcrt.getwch() + if z is unichr(0): + return self.fncodes[code] + else: + return self.navcodes[code] + except KeyError: + pass + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0D, 0) + +elif os.name == "posix": + import atexit + import termios + import fcntl + import signal + + class Console(ConsoleBase): + def __init__(self, miniterm): + super(Console, self).__init__(miniterm) + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + signal.signal(signal.SIGINT, self.sigint) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7F): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b"\0") + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + + def sigint(self, sig, frame): + """signal handler for a clean exit on SIGINT""" + self.miniterm.stop() + self.cancel() + +else: + raise NotImplementedError( + "Sorry no implementation for your platform ({}) available.".format(sys.platform) + ) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +class Transform(object): + """do-nothing: forward all data unchanged""" + + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace("\n", "\r\n") + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace("\r", "\n") + + def tx(self, text): + return text.replace("\n", "\r") + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict( + (x, 0x2400 + x) for x in range(32) if unichr(x) not in "\r\n\b\t" + ) + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + } + ) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + } + ) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if " " <= c < "\x7f" or c in "\r\n\b\t": + r.append(c) + elif c < " ": + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in "{:d}".format(ord(c))) + r.append(" ") + return "".join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = "\x1b[37m" + self.echo_color = "\x1b[31m" + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(" [RX:{!r}] ".format(text)) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(" [TX:{!r}] ".format(text)) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + "crlf": CRLF, + "cr": CR, + "lf": LF, +} + +TRANSFORMATIONS = { + "direct": Transform, # no transformation + "default": NoTerminal, + "nocontrol": NoControls, + "printable": Printable, + "colorize": Colorize, + "debug": DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write("\n--- Available ports:\n") + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write("--- {:2}: {:20} {!r}\n".format(n, port, desc)) + ports.append(port) + while True: + sys.stderr.write("--- Enter port index or full name: ") + port = raw_input("") + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write("--- Invalid index!\n") + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol="crlf", filters=()): + self.console = Console(self) + self.serial = serial_instance + self.echo = True + self.raw = False + self.input_encoding = "UTF-8" + self.output_encoding = "UTF-8" + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = unichr(0x1D) # GS/CTRL+] + self.menu_character = unichr(0x14) # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + self.tx_encoder = None + self.no_write = False + self.remove_no_write = False + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name="rx") + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, "cancel_read"): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name="tx") + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, "cancel_read"): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [ + TRANSFORMATIONS[f] for f in self.filters + ] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors="replace"): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors="replace"): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write( + "\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial + ) + ) + sys.stderr.write( + "--- RTS: {:8} DTR: {:8} BREAK: {:8}\n".format( + ("active" if self.serial.rts else "inactive"), + ("active" if self.serial.dtr else "inactive"), + ("active" if self.serial.break_condition else "inactive"), + ) + ) + try: + sys.stderr.write( + "--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n".format( + ("active" if self.serial.cts else "inactive"), + ("active" if self.serial.dsr else "inactive"), + ("active" if self.serial.ri else "inactive"), + ("active" if self.serial.cd else "inactive"), + ) + ) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write( + "--- software flow control: {}\n".format( + "active" if self.serial.xonxoff else "inactive" + ) + ) + sys.stderr.write( + "--- hardware flow control: {}\n".format( + "active" if self.serial.rtscts else "inactive" + ) + ) + sys.stderr.write("--- serial input encoding: {}\n".format(self.input_encoding)) + sys.stderr.write( + "--- serial output encoding: {}\n".format(self.output_encoding) + ) + sys.stderr.write("--- EOL: {}\n".format(self.eol.upper())) + sys.stderr.write("--- filters: {}\n".format(" ".join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + data = b"" + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + try: + data2 = self.serial.read(self.serial.in_waiting or 1) + except: + data2 = b"" + if data2: + if self.remove_no_write == True: + self.remove_no_write = False + self.no_write = False + data = data2 + else: + data += data2 + if b"\n" in data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + if b"m1\n" in data: + self.no_write = True + if self.no_write == False: + self.console.write(text) + data = b"" + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + self.remove_no_write = True + except KeyboardInterrupt: + c = "\x03" + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + # ~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == "\x15": # CTRL+U -> upload file + self.upload_file() + elif c in "\x08hH?": # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == "\x12": # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write( + "--- RTS {} ---\n".format("active" if self.serial.rts else "inactive") + ) + elif c == "\x04": # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write( + "--- DTR {} ---\n".format("active" if self.serial.dtr else "inactive") + ) + elif c == "\x02": # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write( + "--- BREAK {} ---\n".format( + "active" if self.serial.break_condition else "inactive" + ) + ) + elif c == "\x05": # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write( + "--- local echo {} ---\n".format("active" if self.echo else "inactive") + ) + elif c == "\x06": # CTRL+F -> edit filters + self.change_filter() + elif c == "\x0c": # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write("--- EOL: {} ---\n".format(self.eol.upper())) + self.update_transformations() + elif c == "\x01": # CTRL+A -> set encoding + self.change_encoding() + elif c == "\x09": # CTRL+I -> info + self.dump_port_settings() + # ~ elif c == '\x01': # CTRL+A -> cycle escape mode + # ~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in "pP": # P -> change port + self.change_port() + elif c in "zZ": # S -> suspend / open port temporarily + self.suspend_port() + elif c in "bB": # B -> change baudrate + self.change_baudrate() + elif c == "8": # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == "7": # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in "eE": # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in "oO": # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in "mM": # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in "sS": # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in "nN": # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == "1": # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == "2": # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == "3": # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in "xX": # X -> change software flow control + self.serial.xonxoff = c == "X" + self.dump_port_settings() + elif c in "rR": # R -> change hardware flow control + self.serial.rtscts = c == "R" + self.dump_port_settings() + elif c in "qQ": + self.stop() # Q -> exit app + else: + sys.stderr.write( + "--- unknown menu character {} --\n".format(key_description(c)) + ) + + def upload_file(self): + """Ask user for filename and send its contents""" + sys.stderr.write("\n--- File to upload: ") + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip("\r\n") + if filename: + try: + with open(filename, "rb") as f: + sys.stderr.write("--- Sending file {} ---\n".format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write(".") # Progress indicator. + sys.stderr.write("\n--- File {} sent ---\n".format(filename)) + except IOError as e: + sys.stderr.write( + "--- ERROR opening file {}: {} ---\n".format(filename, e) + ) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write("\n--- Available Filters:\n") + sys.stderr.write( + "\n".join( + "--- {:<10} = {.__doc__}".format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()) + ) + ) + sys.stderr.write( + "\n--- Enter new filter name(s) [{}]: ".format(" ".join(self.filters)) + ) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write("--- unknown filter: {!r}\n".format(f)) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write("--- filters: {}\n".format(" ".join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write( + "\n--- Enter new encoding name [{}]: ".format(self.input_encoding) + ) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write("--- invalid encoding name: {}\n".format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write("--- serial input encoding: {}\n".format(self.input_encoding)) + sys.stderr.write( + "--- serial output encoding: {}\n".format(self.output_encoding) + ) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write("\n--- Baudrate: ") + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write("--- ERROR setting baudrate: {} ---\n".format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write("--- ERROR opening new port: {} ---\n".format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write( + "--- Port changed to: {} ---\n".format(self.serial.port) + ) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write("\n--- Port closed: {} ---\n".format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write( + "--- Quit: {exit} | p: port change | any other key to reconnect ---\n".format( + exit=key_description(self.exit_character) + ) + ) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in "pP": + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write("--- ERROR opening port: {} ---\n".format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write("--- Port opened: {} ---\n".format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program (alias {menu} Q) +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format( + version=getattr(serial, "VERSION", "unknown version"), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description("\x12"), + dtr=key_description("\x04"), + brk=key_description("\x02"), + echo=key_description("\x05"), + info=key_description("\x09"), + upload=key_description("\x15"), + repr=key_description("\x01"), + filter=key_description("\x06"), + eol=key_description("\x0c"), + ) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main( + default_port=None, + default_baudrate=9600, + default_rts=None, + default_dtr=None, + serial_instance=None, +): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port." + ) + + parser.add_argument( + "port", + nargs="?", + help='serial port name ("-" to show port list)', + default=default_port, + ) + + parser.add_argument( + "baudrate", + nargs="?", + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate, + ) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=["N", "E", "O", "S", "M"], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default="N", + ) + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False, + ) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False, + ) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts, + ) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr, + ) + + group.add_argument( + "--non-exclusive", + dest="exclusive", + action="store_false", + help="disable locking for native ports", + default=True, + ) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False, + ) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", + "--echo", + action="store_true", + help="enable local echo (default off)", + default=False, + ) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default="UTF-8", + ) + + group.add_argument( + "-f", + "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[], + ) + + group.add_argument( + "--eol", + choices=["CR", "LF", "CRLF"], + type=lambda c: c.upper(), + help="end of line mode", + default="CRLF", + ) + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False, + ) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar="NUM", + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1D, + ) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar="NUM", + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14, + ) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", + "--quiet", + action="store_true", + help="suppress non-error messages", + default=False, + ) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False, + ) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error("--exit-char can not be the same as --menu-char") + + if args.filter: + if "help" in args.filter: + sys.stderr.write("Available filters:\n") + sys.stderr.write( + "\n".join( + "{:<10} = {.__doc__}".format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()) + ) + ) + sys.stderr.write("\n") + sys.exit(1) + filters = args.filter + else: + filters = ["default"] + + while serial_instance is None: + # no port given on command line -> ask user now + if args.port is None or args.port == "-": + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write("\n") + parser.error("user aborted and port is not given") + else: + if not args.port: + parser.error("port is not given") + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True, + ) + + if not hasattr(serial_instance, "cancel_read"): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write( + "--- forcing DTR {}\n".format( + "active" if args.dtr else "inactive" + ) + ) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write( + "--- forcing RTS {}\n".format( + "active" if args.rts else "inactive" + ) + ) + serial_instance.rts = args.rts + + if isinstance(serial_instance, serial.Serial): + serial_instance.exclusive = args.exclusive + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write("could not open port {!r}: {}\n".format(args.port, e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = "-" + else: + break + + miniterm = Miniterm( + serial_instance, echo=args.echo, eol=args.eol.lower(), filters=filters + ) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write( + "--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n".format( + p=miniterm.serial + ) + ) + sys.stderr.write( + "--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n".format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description("\x08"), + ) + ) + sys.stderr.write( + "--- Specifically modified for managing HLPdataBMS4S in Venus OS ---\n".format( + p=miniterm.serial + ) + ) + sys.stderr.write( + "--- Quit: Ctrl-t q | Local echo: Ctrl-t Ctrl-e ---\n".format( + p=miniterm.serial + ) + ) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == "__main__": + main() From 314bb93f88adf994bd4d0b3bbdf696abf408c416 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 15:00:59 +0200 Subject: [PATCH 107/209] Add support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 --- etc/dbus-serialbattery/battery.py | 3 + etc/dbus-serialbattery/dbus-serialbattery.py | 2 + etc/dbus-serialbattery/seplos.py | 301 +++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 etc/dbus-serialbattery/seplos.py diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 1578a8c9..d3399436 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -14,6 +14,9 @@ class Protection(object): This class holds Warning and alarm states for different types of Checks They are of type integer, 2 represents an Alarm, 1 a Warning, 0 if everything is fine """ + ALARM = 2 + WARNING = 1 + OK = 0 def __init__(self): self.voltage_high: int = None diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index b1a034bd..991e2984 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -29,6 +29,7 @@ from lifepower import Lifepower from lltjbd import LltJbd from renogy import Renogy +from seplos import Seplos # from sinowealth import Sinowealth supported_bms_types = [ @@ -42,6 +43,7 @@ {"bms": LltJbd, "baud": 9600}, {"bms": Renogy, "baud": 9600, "address": b"\x30"}, {"bms": Renogy, "baud": 9600, "address": b"\xF7"}, + {"bms": Seplos, "baud": 19200}, # {"bms": Sinowealth}, ] expected_bms_types = [ diff --git a/etc/dbus-serialbattery/seplos.py b/etc/dbus-serialbattery/seplos.py new file mode 100644 index 00000000..a5319dcf --- /dev/null +++ b/etc/dbus-serialbattery/seplos.py @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +from battery import Protection, Battery, Cell +from utils import * + + +def int_from_hex_ascii(to_decode, signed=False): + return int.from_bytes( + bytes.fromhex(to_decode.decode("ascii")), byteorder="big", signed=signed + ) + + +class Seplos(Battery): + def __init__(self, port, baud, address=0x00): + super(Seplos, self).__init__(port, baud, address) + self.type = self.BATTERYTYPE + self.poll_interval = 5000 + + BATTERYTYPE = "Seplos" + + COMMAND_STATUS = 0x42 + COMMAND_ALARM = 0x44 + COMMAND_PROTOCOL_VERSION = 0x4F + COMMAND_VENDOR_INFO = 0x51 + + @staticmethod + def int_from_1byte_hex_ascii(data: bytes, offset: int, signed=False): + return int.from_bytes( + bytes.fromhex(data[offset : offset + 2].decode("ascii")), + byteorder="big", + signed=signed, + ) + + @staticmethod + def int_from_2byte_hex_ascii(data: bytes, offset: int, signed=False): + return int.from_bytes( + bytes.fromhex(data[offset : offset + 4].decode("ascii")), + byteorder="big", + signed=signed, + ) + + @staticmethod + def get_checksum(frame: bytes) -> int: + """implements the Seplos checksum algorithm, returns 4 bytes""" + checksum = 0 + for b in frame: + checksum += b + checksum %= 0xFFFF + checksum ^= 0xFFFF + checksum += 1 + return checksum + + @staticmethod + def get_info_length(info: bytes) -> int: + """implements the Seplos checksum for the info length""" + lenid = len(info) + if lenid == 0: + return 0 + + lchksum = (lenid & 0xF) + ((lenid >> 4) & 0xF) + ((lenid >> 8) & 0xF) + lchksum %= 16 + lchksum ^= 0xF + lchksum += 1 + + return (lchksum << 12) + lenid + + @staticmethod + def encode_cmd(address: int, cid2: int, info: bytes = b"") -> bytes: + """encodes a command sent to a battery (cid1=0x46)""" + cid1 = 0x46 + + info_length = Seplos.get_info_length(info) + + frame = "{:02X}{:02X}{:02X}{:02X}{:04X}".format( + 0x20, address, cid1, cid2, info_length + ).encode() + frame += info + + checksum = Seplos.get_checksum(frame) + encoded = b"~" + frame + "{:04X}".format(checksum).encode() + b"\r" + return encoded + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + + try: + return self.read_status_data() + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + return False + + def get_settings(self): + # After successful connection get_settings will be call to set up the battery. + # Set the current limits, populate cell count, etc. + # Return True if success, False for failure + + # Uncomment if BMS does not supply capacity + # self.capacity = BATTERY_CAPACITY + self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + + # init the cell array + for _ in range(self.cell_count): + self.cells.append(Cell(False)) + + return True + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (self.poll_interval) + # Return True if success, False for failure + result_status = self.read_status_data() + # sleep(0.5) + result_alarm = self.read_alarm_data() + + return result_status and result_alarm + + @staticmethod + def decode_alarm_byte(data_byte: int, alarm_bit: int, warn_bit: int): + if data_byte & (1 << alarm_bit) != 0: + return Protection.ALARM + if data_byte & (1 << warn_bit) != 0: + return Protection.WARNING + return Protection.OK + + def read_alarm_data(self): + data = self.read_serial_data_seplos( + self.encode_cmd(address=0x00, cid2=self.COMMAND_ALARM, info=b"01") + ) + # check if connection success + if data is False: + return False + + logger.debug("alarm info raw {}".format(data)) + return self.decode_alarm_data(bytes.fromhex(data.decode("ascii"))) + + def decode_alarm_data(self, data: bytes): + logger.debug("alarm info decoded {}".format(data)) + voltage_alarm_byte = data[30] + self.protection.voltage_cell_low = Seplos.decode_alarm_byte( + data_byte=voltage_alarm_byte, alarm_bit=3, warn_bit=2 + ) + # cell high voltage is actually unused because DBUS does not seem to support it, decoding anyway + # c.f. https://github.com/victronenergy/venus/wiki/dbus#battery + self.protection.voltage_cell_high = Seplos.decode_alarm_byte( + data_byte=voltage_alarm_byte, alarm_bit=1, warn_bit=0 + ) + self.protection.voltage_low = Seplos.decode_alarm_byte( + data_byte=voltage_alarm_byte, alarm_bit=7, warn_bit=6 + ) + self.protection.voltage_high = Seplos.decode_alarm_byte( + data_byte=voltage_alarm_byte, alarm_bit=5, warn_bit=4 + ) + + temperature_alarm_byte = data[31] + self.protection.temp_low_charge = Seplos.decode_alarm_byte( + data_byte=temperature_alarm_byte, alarm_bit=3, warn_bit=2 + ) + self.protection.temp_high_charge = Seplos.decode_alarm_byte( + data_byte=temperature_alarm_byte, alarm_bit=1, warn_bit=0 + ) + self.protection.temp_low_discharge = Seplos.decode_alarm_byte( + data_byte=temperature_alarm_byte, alarm_bit=7, warn_bit=6 + ) + self.protection.temp_high_discharge = Seplos.decode_alarm_byte( + data_byte=temperature_alarm_byte, alarm_bit=5, warn_bit=4 + ) + + current_alarm_byte = data[33] + self.protection.current_over = Seplos.decode_alarm_byte( + data_byte=current_alarm_byte, alarm_bit=1, warn_bit=0 + ) + self.protection.current_under = Seplos.decode_alarm_byte( + data_byte=current_alarm_byte, alarm_bit=3, warn_bit=2 + ) + + soc_alarm_byte = data[34] + self.protection.soc_low = Seplos.decode_alarm_byte( + data_byte=soc_alarm_byte, alarm_bit=3, warn_bit=2 + ) + + switch_byte = data[35] + self.discharge_fet = True if switch_byte & 0b01 != 0 else False + self.charge_fet = True if switch_byte & 0b10 != 0 else False + return True + + def read_status_data(self): + logger.debug("read status data") + data = self.read_serial_data_seplos( + self.encode_cmd(address=0x00, cid2=0x42, info=b"01") + ) + + # check if connection success + if data is False: + return False + + cell_count_offset = 4 + voltage_offset = 6 + temps_offset = 72 + self.cell_count = Seplos.int_from_1byte_hex_ascii( + data=data, offset=cell_count_offset + ) + if self.cell_count == len(self.cells): + for i in range(self.cell_count): + voltage = ( + Seplos.int_from_2byte_hex_ascii(data, voltage_offset + i * 4) / 1000 + ) + self.cells[i].voltage = voltage + logger.debug("Voltage cell[{}]={}V".format(i, voltage)) + for i in range(min(4, self.cell_count)): + temp = ( + Seplos.int_from_2byte_hex_ascii(data, temps_offset + i * 4) - 2731 + ) / 10 + self.cells[i].temp = temp + logger.debug("Temp cell[{}]={}°C".format(i, temp)) + + self.temp1 = ( + Seplos.int_from_2byte_hex_ascii(data, temps_offset + 4 * 4) - 2731 + ) / 10 + self.temp2 = ( + Seplos.int_from_2byte_hex_ascii(data, temps_offset + 5 * 4) - 2731 + ) / 10 + self.current = ( + Seplos.int_from_2byte_hex_ascii(data, offset=96, signed=True) / 100 + ) + self.voltage = Seplos.int_from_2byte_hex_ascii(data, offset=100) / 100 + self.capacity_remain = Seplos.int_from_2byte_hex_ascii(data, offset=104) / 100 + self.capacity = Seplos.int_from_2byte_hex_ascii(data, offset=110) / 100 + self.soc = Seplos.int_from_2byte_hex_ascii(data, offset=114) / 10 + self.cycles = Seplos.int_from_2byte_hex_ascii(data, offset=122) + self.hardware_version = "Seplos BMS {} cells".format(self.cell_count) + + logger.debug("Current = {}A , Voltage = {}V".format(self.current, self.voltage)) + logger.debug( + "Capacity = {}/{}Ah , SOC = {}%".format( + self.capacity_remain, self.capacity, self.soc + ) + ) + logger.debug("Cycles = {}".format(self.cycles)) + logger.debug( + "Environment temp = {}°C , Power temp = {}°C".format( + self.temp1, self.temp2 + ) + ) + logger.debug("HW:" + self.hardware_version) + + return True + + @staticmethod + def is_valid_frame(data: bytes) -> bool: + """checks if data contains a valid frame + * minimum length is 18 Byte + * checksum needs to be valid + * also checks for error code as return code in cid2 + * not checked: lchksum + """ + if len(data) < 18: + logger.warning("short read, data={}".format(data)) + return False + + chksum = Seplos.get_checksum(data[1:-5]) + if chksum != Seplos.int_from_2byte_hex_ascii(data, -5): + logger.warning("checksum error") + return False + + cid2 = data[7:9] + if cid2 != b"00": + logger.warning("command returned with error code {}".format(cid2)) + return False + + return True + + def read_serial_data_seplos(self, command): + logger.debug("read serial data seplos") + + with serial.Serial(self.port, baudrate=self.baud_rate, timeout=1) as ser: + ser.flushOutput() + ser.flushInput() + written = ser.write(command) + logger.debug( + "wrote {} bytes to serial port {}, command={}".format( + written, self.port, command + ) + ) + + data = ser.readline() + + if not Seplos.is_valid_frame(data): + return False + + length_pos = 10 + return_data = data[length_pos + 3 : -5] + info_length = Seplos.int_from_2byte_hex_ascii(b"0" + data[length_pos:], 0) + logger.debug( + "return info data of length {} : {}".format(info_length, return_data) + ) + + return return_data From aceeac97c19aeafc755f2a08b573977d0ab451b6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 15:01:28 +0200 Subject: [PATCH 108/209] change flake8 settings --- .flake8 | 26 ++++++++++---------- etc/dbus-serialbattery/dbus-serialbattery.py | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.flake8 b/.flake8 index 5373f0c2..5e3bb3fe 100644 --- a/.flake8 +++ b/.flake8 @@ -1,19 +1,19 @@ [flake8] max-line-length = 120 exclude = - ./etc/dbus-serialbattery/ant.py, - ./etc/dbus-serialbattery/battery_template.py, - ./etc/dbus-serialbattery/daly.py, - ./etc/dbus-serialbattery/dbus-serialbattery.py, - ./etc/dbus-serialbattery/dbushelper.py, - ./etc/dbus-serialbattery/ecs.py, - ./etc/dbus-serialbattery/lifepower.py, - ./etc/dbus-serialbattery/lltjbd.py, - ./etc/dbus-serialbattery/minimalmodbus.py, - ./etc/dbus-serialbattery/mnb.py, - ./etc/dbus-serialbattery/renogy.py, - ./etc/dbus-serialbattery/revov.py, - ./etc/dbus-serialbattery/sinowealth.py, + #./etc/dbus-serialbattery/ant.py, + #./etc/dbus-serialbattery/battery_template.py, + #./etc/dbus-serialbattery/daly.py, + #./etc/dbus-serialbattery/dbus-serialbattery.py, + #./etc/dbus-serialbattery/dbushelper.py, + #./etc/dbus-serialbattery/ecs.py, + #./etc/dbus-serialbattery/lifepower.py, + #./etc/dbus-serialbattery/lltjbd.py, + #./etc/dbus-serialbattery/minimalmodbus.py, + #./etc/dbus-serialbattery/mnb.py, + #./etc/dbus-serialbattery/renogy.py, + #./etc/dbus-serialbattery/revov.py, + #./etc/dbus-serialbattery/sinowealth.py, ./etc/dbus-serialbattery/test_max17853.py, ./etc/dbus-serialbattery/util_max17853.py, ./velib_python diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 991e2984..f91f0bea 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -4,7 +4,7 @@ from time import sleep from dbus.mainloop.glib import DBusGMainLoop -from threading import Thread +# from threading import Thread ## removed with https://github.com/Louisvdw/dbus-serialbattery/pull/582 import sys if sys.version_info.major == 2: @@ -104,7 +104,7 @@ def get_port() -> str: This prevent problems when using the driver only with a serial connection """ if port == "Jkbms_Ble": - from jkbms_ble import Jkbms_Ble + from jkbms_ble import Jkbms_Ble # noqa: F401 (ignore flake "imported but unused" error) class_ = eval(port) testbms = class_("", 9600, sys.argv[2]) From 64a08f41d8c09200b2bda12b333b928980949a18 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 15:10:31 +0200 Subject: [PATCH 109/209] fix black lint errors --- etc/dbus-serialbattery/battery.py | 1 + etc/dbus-serialbattery/daly.py | 19 ++++++++++++------- etc/dbus-serialbattery/dbus-serialbattery.py | 7 +++++-- etc/dbus-serialbattery/utils.py | 6 +++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index d3399436..e86c7e2e 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -14,6 +14,7 @@ class Protection(object): This class holds Warning and alarm states for different types of Checks They are of type integer, 2 represents an Alarm, 1 a Warning, 0 if everything is fine """ + ALARM = 2 WARNING = 1 OK = 0 diff --git a/etc/dbus-serialbattery/daly.py b/etc/dbus-serialbattery/daly.py index 492b0500..0abeb79e 100644 --- a/etc/dbus-serialbattery/daly.py +++ b/etc/dbus-serialbattery/daly.py @@ -247,9 +247,9 @@ def read_cells_volts(self, ser): buffer[2] = self.command_cell_volts[0] if (int(self.cell_count) % 3) == 0: - maxFrame = (int(self.cell_count / 3)) + maxFrame = int(self.cell_count / 3) else: - maxFrame = (int(self.cell_count / 3) + 1) + maxFrame = int(self.cell_count / 3) + 1 lenFixed = ( maxFrame * 13 ) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + chksum @@ -279,10 +279,15 @@ def read_cells_volts(self, ser): ): # we at least need 4 bytes to extract the identifiers b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: - frame, frameCell[0], frameCell[1], frameCell[2], _, chk = unpack_from( - ">BhhhBB", cells_volts_data, bufIdx + 4 - ) - if sum(cells_volts_data[bufIdx:bufIdx+12]) & 0xFF != chk: + ( + frame, + frameCell[0], + frameCell[1], + frameCell[2], + _, + chk, + ) = unpack_from(">BhhhBB", cells_volts_data, bufIdx + 4) + if sum(cells_volts_data[bufIdx : bufIdx + 12]) & 0xFF != chk: logger.warning("bad cell voltages checksum") return False for idx in range(3): @@ -367,7 +372,7 @@ def read_serial_data_daly(self, ser, command): start, flag, command_ret, length = unpack_from("BBBB", data) checksum = sum(data[:-1]) & 0xFF - if start == 165 and length == 8 and len(data)>12 and checksum == data[12]: + if start == 165 and length == 8 and len(data) > 12 and checksum == data[12]: return data[4 : length + 4] else: logger.error(">>> ERROR: Incorrect Reply") diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index f91f0bea..1fdd7bf1 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -4,6 +4,7 @@ from time import sleep from dbus.mainloop.glib import DBusGMainLoop + # from threading import Thread ## removed with https://github.com/Louisvdw/dbus-serialbattery/pull/582 import sys @@ -30,6 +31,7 @@ from lltjbd import LltJbd from renogy import Renogy from seplos import Seplos + # from sinowealth import Sinowealth supported_bms_types = [ @@ -37,7 +39,7 @@ {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Ecs, "baud": 19200}, - {'bms': HLPdataBMS4S, "baud": 9600}, + {"bms": HLPdataBMS4S, "baud": 9600}, {"bms": Jkbms, "baud": 115200}, {"bms": Lifepower, "baud": 9600}, {"bms": LltJbd, "baud": 9600}, @@ -104,7 +106,8 @@ def get_port() -> str: This prevent problems when using the driver only with a serial connection """ if port == "Jkbms_Ble": - from jkbms_ble import Jkbms_Ble # noqa: F401 (ignore flake "imported but unused" error) + # noqa: F401 --> ignore flake "imported but unused" error + from jkbms_ble import Jkbms_Ble # noqa: F401 class_ = eval(port) testbms = class_("", 9600, sys.argv[2]) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 7cf6eac6..04ac14ea 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -442,7 +442,11 @@ def read_serialport_data( count = 0 data = bytearray(res) - packetlen = length_fixed if length_fixed is not None else length_pos + length_byte_size + length + length_check + packetlen = ( + length_fixed + if length_fixed is not None + else length_pos + length_byte_size + length + length_check + ) while len(data) < packetlen: res = ser.read(packetlen - len(data)) data.extend(res) From 31647db46a7f11464ca3564855499e8f30367a60 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:17:40 +0200 Subject: [PATCH 110/209] removed wildcard imports --- etc/dbus-serialbattery/ant.py | 24 ++++++++------- etc/dbus-serialbattery/battery_template.py | 18 ++++++----- etc/dbus-serialbattery/daly.py | 34 +++++++++++--------- etc/dbus-serialbattery/lifepower.py | 24 ++++++++++----- etc/dbus-serialbattery/lltjbd.py | 36 +++++++++++++--------- etc/dbus-serialbattery/renogy.py | 24 ++++++++------- etc/dbus-serialbattery/sinowealth.py | 24 +++++++++------ etc/dbus-serialbattery/utils.py | 2 +- 8 files changed, 109 insertions(+), 77 deletions(-) diff --git a/etc/dbus-serialbattery/ant.py b/etc/dbus-serialbattery/ant.py index 07b58d11..fde6d612 100644 --- a/etc/dbus-serialbattery/ant.py +++ b/etc/dbus-serialbattery/ant.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery +from utils import read_serial_data, logger +import utils +from struct import unpack_from class Ant(Battery): @@ -25,8 +26,9 @@ def test_connection(self): result = False try: result = self.read_status_data() - except: - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result @@ -34,8 +36,8 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT self.version = "ANT BMS V2.0" logger.info(self.hardware_version) return True @@ -59,8 +61,8 @@ def read_status_data(self): self.current = 0.0 if current == 0 else current / -10 self.cell_count = unpack_from(">b", status_data, 123)[0] - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count cell_max_no, cell_max_voltage, cell_min_no, cell_min_voltage = unpack_from( ">bhbh", status_data, 115 @@ -95,9 +97,9 @@ def read_status_data(self): ) self.protection.voltage_cell_low = ( 2 - if self.cell_min_voltage < MIN_CELL_VOLTAGE - 0.1 + if self.cell_min_voltage < utils.MIN_CELL_VOLTAGE - 0.1 else 1 - if self.cell_min_voltage < MIN_CELL_VOLTAGE + if self.cell_min_voltage < utils.MIN_CELL_VOLTAGE else 0 ) self.protection.temp_high_charge = ( diff --git a/etc/dbus-serialbattery/battery_template.py b/etc/dbus-serialbattery/battery_template.py index e921efc0..10b851ee 100644 --- a/etc/dbus-serialbattery/battery_template.py +++ b/etc/dbus-serialbattery/battery_template.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from battery import Protection, Battery, Cell -from utils import * -from struct import * +from utils import is_bit_set, read_serial_data, logger +import utils +from struct import unpack_from class BatteryTemplate(Battery): @@ -20,8 +21,9 @@ def test_connection(self): result = False try: result = self.read_status_data() - except: - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result @@ -32,10 +34,10 @@ def get_settings(self): # Uncomment if BMS does not supply capacity # self.capacity = BATTERY_CAPACITY - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count return True def refresh_data(self): diff --git a/etc/dbus-serialbattery/daly.py b/etc/dbus-serialbattery/daly.py index 0abeb79e..d91ae568 100644 --- a/etc/dbus-serialbattery/daly.py +++ b/etc/dbus-serialbattery/daly.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery, Cell +from utils import open_serial_port, read_serialport_data, logger +import utils +from struct import unpack_from class Daly(Battery): @@ -37,21 +38,26 @@ def __init__(self, port, baud, address): TEMP_ZERO_CONSTANT = 40 def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure result = False try: ser = open_serial_port(self.port, self.baud_rate) if ser is not None: result = self.read_status_data(ser) ser.close() - except: - pass + + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result def get_settings(self): - self.capacity = BATTERY_CAPACITY - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.capacity = utils.BATTERY_CAPACITY + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT return True def refresh_data(self): @@ -95,8 +101,8 @@ def read_status_data(self, ser): self.cycles, ) = unpack_from(">bb??bhx", status_data) - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count self.hardware_version = "DalyBMS " + str(self.cell_count) + " cells" logger.info(self.hardware_version) @@ -104,8 +110,8 @@ def read_status_data(self, ser): def read_soc_data(self, ser): # Ensure data received is valid - crntMinValid = -(MAX_BATTERY_DISCHARGE_CURRENT * 2.1) - crntMaxValid = MAX_BATTERY_CHARGE_CURRENT * 1.3 + crntMinValid = -(utils.MAX_BATTERY_DISCHARGE_CURRENT * 2.1) + crntMaxValid = utils.MAX_BATTERY_CHARGE_CURRENT * 1.3 triesValid = 2 while triesValid > 0: soc_data = self.read_serial_data_daly(ser, self.command_soc) @@ -117,7 +123,7 @@ def read_soc_data(self, ser): current = ( (current - self.CURRENT_ZERO_CONSTANT) / -10 - * INVERT_CURRENT_MEASUREMENT + * utils.INVERT_CURRENT_MEASUREMENT ) if crntMinValid < current < crntMaxValid: self.voltage = voltage / 10 @@ -262,7 +268,7 @@ def read_cells_volts(self, ser): return False frameCell = [0, 0, 0] - lowMin = MIN_CELL_VOLTAGE / 2 + lowMin = utils.MIN_CELL_VOLTAGE / 2 frame = 0 bufIdx = 0 diff --git a/etc/dbus-serialbattery/lifepower.py b/etc/dbus-serialbattery/lifepower.py index cc3058c0..84c93a37 100644 --- a/etc/dbus-serialbattery/lifepower.py +++ b/etc/dbus-serialbattery/lifepower.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery, Cell +from utils import read_serial_data, logger +import utils +from struct import unpack_from import re @@ -24,14 +25,21 @@ def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. # The result or call should be unique to this BMS. Battery name or version, etc. # Return True if success, False for failure - return self.read_status_data() + result = False + try: + result = self.read_status_data() + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False + + return result def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_current = MAX_BATTERY_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_current = utils.MAX_BATTERY_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT hardware_version = self.read_serial_data_eg4(self.command_hardware_version) if hardware_version: # I get some characters that I'm not able to figure out the encoding, probably chinese so I discard it @@ -89,8 +97,8 @@ def read_status_data(self): # Cells self.cell_count = len(groups[0]) - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count self.cells = [Cell(True) for _ in range(0, self.cell_count)] for i, cell in enumerate(self.cells): diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index a8222fd6..29ac7068 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- from battery import Protection, Battery, Cell -from utils import * -from struct import * +from utils import is_bit_set, read_serial_data, logger +import utils +from struct import unpack_from +import struct class LltJbdProtection(Protection): @@ -59,18 +61,22 @@ def __init__(self, port, baud, address): LENGTH_POS = 3 def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure result = False try: result = self.read_hardware_data() - except: - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result def get_settings(self): self.read_gen_data() - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT return True def refresh_data(self): @@ -79,7 +85,7 @@ def refresh_data(self): return result def to_protection_bits(self, byte_data): - tmp = bin(byte_data)[2:].rjust(13, zero_char) + tmp = bin(byte_data)[2:].rjust(13, utils.zero_char) self.protection.voltage_high = 2 if is_bit_set(tmp[10]) else 0 self.protection.voltage_low = 2 if is_bit_set(tmp[9]) else 0 @@ -92,7 +98,7 @@ def to_protection_bits(self, byte_data): # Software implementations for low soc self.protection.soc_low = ( - 2 if self.soc < SOC_LOW_ALARM else 1 if self.soc < SOC_LOW_WARNING else 0 + 2 if self.soc < utils.SOC_LOW_ALARM else 1 if self.soc < utils.SOC_LOW_WARNING else 0 ) # extra protection flags for LltJbd @@ -107,17 +113,17 @@ def to_cell_bits(self, byte_data, byte_data_high): for c in self.cells: self.cells.remove(c) # get up to the first 16 cells - tmp = bin(byte_data)[2:].rjust(min(self.cell_count, 16), zero_char) + tmp = bin(byte_data)[2:].rjust(min(self.cell_count, 16), utils.zero_char) for bit in reversed(tmp): self.cells.append(Cell(is_bit_set(bit))) # get any cells above 16 if self.cell_count > 16: - tmp = bin(byte_data_high)[2:].rjust(self.cell_count - 16, zero_char) + tmp = bin(byte_data_high)[2:].rjust(self.cell_count - 16, utils.zero_char) for bit in reversed(tmp): self.cells.append(Cell(is_bit_set(bit))) def to_fet_bits(self, byte_data): - tmp = bin(byte_data)[2:].rjust(2, zero_char) + tmp = bin(byte_data)[2:].rjust(2, utils.zero_char) self.charge_fet = is_bit_set(tmp[1]) self.discharge_fet = is_bit_set(tmp[0]) @@ -152,15 +158,15 @@ def read_gen_data(self): self.version = float(str(version >> 4 & 0x0F) + "." + str(version & 0x0F)) self.to_fet_bits(fet) self.to_protection_bits(protection) - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count for t in range(self.temp_sensors): temp1 = unpack_from(">H", gen_data, 23 + (2 * t))[0] if t == 0: - self.to_temp("mos", kelvin_to_celsius(temp1 / 10)) + self.to_temp("mos", utils.kelvin_to_celsius(temp1 / 10)) else: - self.to_temp(t, kelvin_to_celsius(temp1 / 10)) + self.to_temp(t, utils.kelvin_to_celsius(temp1 / 10)) return True diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/renogy.py index fe493da6..ea513f23 100644 --- a/etc/dbus-serialbattery/renogy.py +++ b/etc/dbus-serialbattery/renogy.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * +from battery import Battery, Cell +from utils import read_serial_data, unpack, unpack_from, logger +import utils import struct @@ -16,7 +17,8 @@ def __init__(self, port, baud, address): LENGTH_CHECK = 4 LENGTH_POS = 2 - # command bytes [Address field][Function code (03 = Read register)][Register Address (2 bytes)][Data Length (2 bytes)][CRC (2 bytes little endian)] + # command bytes [Address field][Function code (03 = Read register)] + # [Register Address (2 bytes)][Data Length (2 bytes)][CRC (2 bytes little endian)] command_read = b"\x03" # Core data = voltage, temp, current, soc command_cell_count = b"\x13\x88\x00\x01" # Register 5000 @@ -44,9 +46,9 @@ def test_connection(self): result = False try: result = self.read_gen_data() - except: - logger.exception("Unexpected exception encountered") - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result @@ -54,11 +56,11 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count return True def refresh_data(self): @@ -203,7 +205,7 @@ def read_serial_data_renogy(self, command): return False start, flag, length = unpack_from("BBB", data) - checksum = unpack_from(">H", data, length + 3) + # checksum = unpack_from(">H", data, length + 3) if flag == 3: return data[3 : length + 3] diff --git a/etc/dbus-serialbattery/sinowealth.py b/etc/dbus-serialbattery/sinowealth.py index f5021ff5..60b41895 100755 --- a/etc/dbus-serialbattery/sinowealth.py +++ b/etc/dbus-serialbattery/sinowealth.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery, Cell +from utils import kelvin_to_celsius, read_serial_data, logger +import utils +from struct import unpack_from class Sinowealth(Battery): @@ -33,25 +34,30 @@ def __init__(self, port, baud, address): LENGTH_POS = 0 def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure result = False try: result = self.read_status_data() result = result and self.read_remaining_capacity() result = result and self.read_pack_config_data() - except: - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False + return result def get_settings(self): # hardcoded parameters, to be requested from the BMS in the future - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT if self.cell_count is None: self.read_pack_config_data() - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count self.hardware_version = "Daly/Sinowealth BMS " + str(self.cell_count) + " cells" logger.info(self.hardware_version) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 04ac14ea..d90bbfbb 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230426)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230427)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 8d3f25e5cacf340385191f59d23fb740c2160f8f Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:33:09 +0200 Subject: [PATCH 111/209] fixed black lint errors --- etc/dbus-serialbattery/hlpdatabms4s.py | 23 ++++++++++--------- .../hlpdatabms4s_miniterm.py | 13 +++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/etc/dbus-serialbattery/hlpdatabms4s.py b/etc/dbus-serialbattery/hlpdatabms4s.py index 63995357..6fcbc6b0 100644 --- a/etc/dbus-serialbattery/hlpdatabms4s.py +++ b/etc/dbus-serialbattery/hlpdatabms4s.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import * -from struct import * +from battery import Battery, Cell +from utils import logger +import utils import serial from time import sleep @@ -20,9 +20,10 @@ def test_connection(self): result = False try: result = self.read_test_data() - except Exception as e: - # logger.error(e, exc_info=True) - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False + return result def get_settings(self): @@ -33,7 +34,7 @@ def get_settings(self): try: result = self.read_settings_data() except Exception as e: - # logger.error(e, exc_info=True) + logger.error(e, exc_info=True) pass return result @@ -45,7 +46,7 @@ def refresh_data(self): try: result = self.read_status_data() except Exception as e: - # logger.error(e, exc_info=True) + logger.error(e, exc_info=True) pass return result @@ -71,8 +72,8 @@ def read_test_data(self): if ix > 0: self.hardware_version = s1[ix : len(s1) - 1] self.version = self.hardware_version - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT self.poll_interval = 10000 self.control_discharge_current = 1000 self.control_charge_current = 1000 @@ -201,7 +202,7 @@ def read_serial_data2(command, port, baud, time, min_len): try: with serial.Serial(port, baudrate=baud, timeout=0.5) as ser: ret = read_serialport_data2(ser, command, time, min_len) - if not ret is False: + if False is not ret: return ret return False diff --git a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py b/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py index 741ac75a..22df77fd 100644 --- a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py +++ b/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py @@ -19,7 +19,6 @@ import serial from serial.tools.list_ports import comports from serial.tools import hexlify_codec -from time import sleep # pylint: disable=wrong-import-order,wrong-import-position @@ -549,10 +548,10 @@ def reader(self): # read all that is there or wait for one byte try: data2 = self.serial.read(self.serial.in_waiting or 1) - except: + except Exception: data2 = b"" if data2: - if self.remove_no_write == True: + if self.remove_no_write is True: self.remove_no_write = False self.no_write = False data = data2 @@ -567,7 +566,7 @@ def reader(self): text = transformation.rx(text) if b"m1\n" in data: self.no_write = True - if self.no_write == False: + if self.no_write is False: self.console.write(text) data = b"" @@ -606,7 +605,7 @@ def writer(self): for transformation in self.tx_transformations: echo_text = transformation.echo(echo_text) self.console.write(echo_text) - except: + except Exception: self.alive = False raise @@ -1163,12 +1162,12 @@ def main( ) ) sys.stderr.write( - "--- Specifically modified for managing HLPdataBMS4S in Venus OS ---\n".format( + "--- Specifically modified for managing HLPdataBMS4S in Venus OS ---\n".format( # noqa: F522 p=miniterm.serial ) ) sys.stderr.write( - "--- Quit: Ctrl-t q | Local echo: Ctrl-t Ctrl-e ---\n".format( + "--- Quit: Ctrl-t q | Local echo: Ctrl-t Ctrl-e ---\n".format( # noqa: F522 p=miniterm.serial ) ) From a4a3efbb19ea9984ce9870c146c843e8fe775a33 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:33:30 +0200 Subject: [PATCH 112/209] change flake8 settings --- .flake8 | 10 +++++----- CHANGELOG.md | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 5e3bb3fe..fcc7f0bf 100644 --- a/.flake8 +++ b/.flake8 @@ -2,17 +2,17 @@ max-line-length = 120 exclude = #./etc/dbus-serialbattery/ant.py, - #./etc/dbus-serialbattery/battery_template.py, + ./etc/dbus-serialbattery/battery_template.py, #./etc/dbus-serialbattery/daly.py, #./etc/dbus-serialbattery/dbus-serialbattery.py, - #./etc/dbus-serialbattery/dbushelper.py, + ./etc/dbus-serialbattery/dbushelper.py, #./etc/dbus-serialbattery/ecs.py, #./etc/dbus-serialbattery/lifepower.py, #./etc/dbus-serialbattery/lltjbd.py, - #./etc/dbus-serialbattery/minimalmodbus.py, - #./etc/dbus-serialbattery/mnb.py, + ./etc/dbus-serialbattery/minimalmodbus.py, + ./etc/dbus-serialbattery/mnb.py, #./etc/dbus-serialbattery/renogy.py, - #./etc/dbus-serialbattery/revov.py, + ./etc/dbus-serialbattery/revov.py, #./etc/dbus-serialbattery/sinowealth.py, ./etc/dbus-serialbattery/test_max17853.py, ./etc/dbus-serialbattery/util_max17853.py, diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d2ee32..9aa162c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ * Added: Show charge/discharge limitation reason * Added: Show specific TimeToSoC points in GUI, if 0%, 10%, 20%, 80%, 90% and/or 100% are selected * Added: Show TimeToGo in GUI only, if enabled +* Added: Support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 by @peterohman +* Added: Support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 by @wollew * Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) * Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository * Changed: Added QML to `restoregui.sh` From 4a26d0d5f4ce9f60058848eef0969cff67d6612f Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:39:58 +0200 Subject: [PATCH 113/209] remove wildcard import and fix black lint errors --- etc/dbus-serialbattery/lltjbd.py | 6 +++++- etc/dbus-serialbattery/seplos.py | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/lltjbd.py index 29ac7068..c285d44a 100644 --- a/etc/dbus-serialbattery/lltjbd.py +++ b/etc/dbus-serialbattery/lltjbd.py @@ -98,7 +98,11 @@ def to_protection_bits(self, byte_data): # Software implementations for low soc self.protection.soc_low = ( - 2 if self.soc < utils.SOC_LOW_ALARM else 1 if self.soc < utils.SOC_LOW_WARNING else 0 + 2 + if self.soc < utils.SOC_LOW_ALARM + else 1 + if self.soc < utils.SOC_LOW_WARNING + else 0 ) # extra protection flags for LltJbd diff --git a/etc/dbus-serialbattery/seplos.py b/etc/dbus-serialbattery/seplos.py index a5319dcf..1adf12bf 100644 --- a/etc/dbus-serialbattery/seplos.py +++ b/etc/dbus-serialbattery/seplos.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from battery import Protection, Battery, Cell -from utils import * +from utils import logger +import utils +import serial def int_from_hex_ascii(to_decode, signed=False): @@ -97,10 +99,10 @@ def get_settings(self): # Uncomment if BMS does not supply capacity # self.capacity = BATTERY_CAPACITY - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count # init the cell array for _ in range(self.cell_count): From 564d4edc151500bdf00dac54f76ffd9822b51893 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:47:43 +0200 Subject: [PATCH 114/209] removed wildcard import --- etc/dbus-serialbattery/ecs.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/etc/dbus-serialbattery/ecs.py b/etc/dbus-serialbattery/ecs.py index d8bc7295..1777e6f0 100644 --- a/etc/dbus-serialbattery/ecs.py +++ b/etc/dbus-serialbattery/ecs.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals from battery import Protection, Battery, Cell -from utils import * -from struct import * +from utils import logger +import utils import minimalmodbus @@ -33,7 +33,7 @@ def test_connection(self): # Trying to find Green Meter ID try: - mbdev = minimalmodbus.Instrument(self.port, GREENMETER_ADDRESS) + mbdev = minimalmodbus.Instrument(self.port, utils.GREENMETER_ADDRESS) mbdev.serial.parity = minimalmodbus.serial.PARITY_EVEN tmpId = mbdev.read_register(0, 0) if tmpId in range(self.GREENMETER_ID_500A, self.GREENMETER_ID_125A + 1): @@ -52,7 +52,7 @@ def test_connection(self): def find_LiPro_cells(self): # test for LiPro cell devices - for cell_address in range(LIPRO_START_ADDRESS, LIPRO_END_ADDRESS + 1): + for cell_address in range(utils.LIPRO_START_ADDRESS, utils.LIPRO_END_ADDRESS + 1): try: mbdev = minimalmodbus.Instrument(self.port, cell_address) mbdev.serial.parity = minimalmodbus.serial.PARITY_EVEN @@ -74,11 +74,11 @@ def get_settings(self): # Return True if success, False for failure # Uncomment if BMS does not supply capacity - self.max_battery_charge_current = MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = MAX_BATTERY_DISCHARGE_CURRENT - self.cell_count = LIPRO_CELL_COUNT - self.max_battery_voltage = MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = MIN_CELL_VOLTAGE * self.cell_count + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT + self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.cell_count = utils.LIPRO_CELL_COUNT + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count self.temp_sensors = 2 return self.read_status_data() @@ -94,7 +94,7 @@ def refresh_data(self): def read_status_data(self): try: - mbdev = minimalmodbus.Instrument(self.port, GREENMETER_ADDRESS) + mbdev = minimalmodbus.Instrument(self.port, utils.GREENMETER_ADDRESS) mbdev.serial.parity = minimalmodbus.serial.PARITY_EVEN self.max_battery_discharge_current = abs( @@ -120,7 +120,7 @@ def read_status_data(self): def read_soc_data(self): try: - mbdev = minimalmodbus.Instrument(self.port, GREENMETER_ADDRESS) + mbdev = minimalmodbus.Instrument(self.port, utils.GREENMETER_ADDRESS) mbdev.serial.parity = minimalmodbus.serial.PARITY_EVEN self.voltage = ( From 41cdadb837e83fc5b336f748f6cfd3fb03a7b813 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 17:53:00 +0200 Subject: [PATCH 115/209] fixed black lint check --- etc/dbus-serialbattery/ecs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/ecs.py b/etc/dbus-serialbattery/ecs.py index 1777e6f0..41065b7e 100644 --- a/etc/dbus-serialbattery/ecs.py +++ b/etc/dbus-serialbattery/ecs.py @@ -52,7 +52,9 @@ def test_connection(self): def find_LiPro_cells(self): # test for LiPro cell devices - for cell_address in range(utils.LIPRO_START_ADDRESS, utils.LIPRO_END_ADDRESS + 1): + for cell_address in range( + utils.LIPRO_START_ADDRESS, utils.LIPRO_END_ADDRESS + 1 + ): try: mbdev = minimalmodbus.Instrument(self.port, cell_address) mbdev.serial.parity = minimalmodbus.serial.PARITY_EVEN From 440b113100f6b4ae051103f63a26ee5a53ec28e6 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 18:23:26 +0200 Subject: [PATCH 116/209] config changes --- .flake8 | 7 ---- .gitignore | 5 ++- CHANGELOG.md | 104 ++++++++++++++++++++++++++------------------------- 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/.flake8 b/.flake8 index fcc7f0bf..85d1bd06 100644 --- a/.flake8 +++ b/.flake8 @@ -1,19 +1,12 @@ [flake8] max-line-length = 120 exclude = - #./etc/dbus-serialbattery/ant.py, ./etc/dbus-serialbattery/battery_template.py, - #./etc/dbus-serialbattery/daly.py, #./etc/dbus-serialbattery/dbus-serialbattery.py, ./etc/dbus-serialbattery/dbushelper.py, - #./etc/dbus-serialbattery/ecs.py, - #./etc/dbus-serialbattery/lifepower.py, - #./etc/dbus-serialbattery/lltjbd.py, ./etc/dbus-serialbattery/minimalmodbus.py, ./etc/dbus-serialbattery/mnb.py, - #./etc/dbus-serialbattery/renogy.py, ./etc/dbus-serialbattery/revov.py, - #./etc/dbus-serialbattery/sinowealth.py, ./etc/dbus-serialbattery/test_max17853.py, ./etc/dbus-serialbattery/util_max17853.py, ./velib_python diff --git a/.gitignore b/.gitignore index 287a9c8b..59bf8fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -151,6 +151,9 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +# VS Code +.vscode/ + # Custom for this repo venus-data.tar.gz BMS-trials @@ -161,4 +164,4 @@ BMS-trials etc/dbus-serialbattery/config.ini # Local Clone of velib_python -velib_python \ No newline at end of file +velib_python diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aa162c6..ba0d4aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,58 +2,60 @@ ## v1.0.0-jkbms_ble -* Added: Balancing status for JKBMS -* Added: Balancing switch status for JKBMS -* Added: Balancing switch status to the GUI -> SerialBattery -> IO -* Added: Charge Mode display -* Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) -* Added: Create empty `config.ini` for easier user usage -* Added: Cronjob to restart Bluetooth service every 12 hours -* Added: Driver uninstall script -* Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 -* Added: HighInternalTemperature alarm (MOSFET) for JKBMS -* Added: Install needed components automatically after a Venus OS upgrade -* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 -* Added: JKBMS BLE - Balancing switch status -* Added: JKBMS BLE - Capacity -* Added: JKBMS BLE - Cell imbalance alert -* Added: JKBMS BLE - Charging switch status -* Added: JKBMS BLE - Discharging switch status -* Added: JKBMS BLE - MOS temperature -* Added: JKBMS BLE - Show if balancing is active and which cells are balancing -* Added: Post install notes -* Added: Script to install directly from repository -* Added: Show charge mode (absorption, bulk, ...) in Parameters page -* Added: Show charge/discharge limitation reason -* Added: Show specific TimeToSoC points in GUI, if 0%, 10%, 20%, 80%, 90% and/or 100% are selected -* Added: Show TimeToGo in GUI only, if enabled +* Added: Balancing status for JKBMS by @mr-manuel +* Added: Balancing switch status for JKBMS by @mr-manuel +* Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel +* Added: Charge Mode display by @mr-manuel +* Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel +* Added: Config file by @ppuetsch +* Added: Create empty `config.ini` for easier user usage by @mr-manuel +* Added: Cronjob to restart Bluetooth service every 12 hours by @mr-manuel +* Added: Driver uninstall script by @mr-manuel +* Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel +* Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel +* Added: Install needed components automatically after a Venus OS upgrade by @mr-manuel +* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @mr-manuel +* Added: JKBMS BLE - Balancing switch status by @mr-manuel +* Added: JKBMS BLE - Capacity by @mr-manuel +* Added: JKBMS BLE - Cell imbalance alert by @mr-manuel +* Added: JKBMS BLE - Charging switch status by @mr-manuel +* Added: JKBMS BLE - Discharging switch status by @mr-manuel +* Added: JKBMS BLE - MOS temperature by @mr-manuel +* Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel +* Added: Post install notes by @mr-manuel +* Added: Script to install directly from repository by @mr-manuel +* Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel +* Added: Show charge/discharge limitation reason by @mr-manuel +* Added: Show specific TimeToSoC points in GUI, if 0%, 10%, 20%, 80%, 90% and/or 100% are selected by @mr-manuel +* Added: Show TimeToGo in GUI only, if enabled by @mr-manuel * Added: Support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 by @peterohman * Added: Support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 by @wollew -* Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) -* Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository -* Changed: Added QML to `restoregui.sh` -* Changed: Bash output -* Changed: Default config file - * Added missing descriptions to make it much clearer to understand - * Changed name from `default_config.ini` to `config.default.ini` https://github.com/Louisvdw/dbus-serialbattery/pull/412#issuecomment-1434287942 - * Changed TimeToSoc default value `TIME_TO_SOC_VALUE_TYPE` from `Both seconds and time string " [d h m s]"` to `1 Seconds` - * Changed TimeToSoc description - * Changed value positions, added groups and much clearer descriptions -* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 -* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 -* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 +* Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) by @mr-manuel +* Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository by @mr-manuel +* Changed: Added QML to `restoregui.sh` by @mr-manuel +* Changed: Bash output by @mr-manuel +* Changed: Default config file by @mr-manuel + * Added missing descriptions to make it much clearer to understand by @mr-manuel + * Changed name from `default_config.ini` to `config.default.ini` https://github.com/Louisvdw/dbus-serialbattery/pull/412#issuecomment-1434287942 by @mr-manuel + * Changed TimeToSoc default value `TIME_TO_SOC_VALUE_TYPE` from `Both seconds and time string " [d h m s]"` to `1 Seconds` by @mr-manuel + * Changed TimeToSoc description by @mr-manuel + * Changed value positions, added groups and much clearer descriptions by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/397 by @transistorgit -* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 -* Changed: Fixed black lint errors -* Changed: Fixed cell balancing background for cells 17-24 -* Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 by @mr-manuel +* Changed: Fixed black lint errors by @mr-manuel +* Changed: Fixed cell balancing background for cells 17-24 by @mr-manuel +* Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel * Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek -* Changed: Logging to get relevant data -* Changed: Moved ble part to `installble.sh` -* Changed: Optimized installation scripts -* Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 -* Changed: Separate Time-To-Go and Time-To-SoC activation -* Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS -* Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` -* Changed: Time-To-Soc string from `days, HR:MN:SC` to `d h m s` (same as Time-To-Go) -* Changed: Uninstall also installed Bluetooth modules on uninstall. +* Changed: Logging to get relevant data by @mr-manuel +* Changed: Moved ble part to `installble.sh` by @mr-manuel +* Changed: Optimized installation scripts by @mr-manuel +* Changed: Removed wildcard imports from several BMS drivers and fixed black lint errors by @mr-manuel +* Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 by @mr-manuel +* Changed: Separate Time-To-Go and Time-To-SoC activation by @mr-manuel +* Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS by @mr-manuel +* Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` by @mr-manuel +* Changed: Time-To-Soc string from `days, HR:MN:SC` to `d h m s` (same as Time-To-Go) by @mr-manuel +* Changed: Uninstall also installed Bluetooth modules on uninstall. by @mr-manuel From bc91973ca9d4fb5af8ea446b36cb2f29dee397cd Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 18:53:07 +0200 Subject: [PATCH 117/209] remove old log message in handle_changed_setting() --- etc/dbus-serialbattery/dbushelper.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 36b3c819..0a629778 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -94,10 +94,6 @@ def handle_changed_setting(self, setting, oldvalue, newvalue): self.battery.role, self.instance = self.get_role_instance() logger.info("Changed DeviceInstance = %d", self.instance) return - logger.info( - "Changed DeviceInstance = %d", float(self.settings["CellVoltageMin"]) - ) - # self._dbusservice['/History/ChargeCycles'] def setup_vedbus(self): # Set up dbus service and device instance From 83668b62b558af7b052d93d570588e42f531d8c4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 19:09:18 +0200 Subject: [PATCH 118/209] simplified condition for Time-To-Go/Soc --- etc/dbus-serialbattery/dbushelper.py | 14 +++----------- etc/dbus-serialbattery/mnb.py | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 0a629778..246a1aad 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -498,22 +498,14 @@ def publish_dbus(self): except: pass - # Update TimeToSoC + # Update TimeToGo and/or TimeToSoC try: if ( self.battery.capacity is not None and (TIME_TO_GO_ENABLE or len(TIME_TO_SOC_POINTS) > 0) and ( - ( - # update only once in same second - int(time()) != self.battery.time_to_soc_update - and - # update only every x seconds - int(time()) % TIME_TO_SOC_RECALCULATE_EVERY == 0 - ) - or - # update on first run - self.battery.time_to_soc_update == 0 + int(time()) - self.battery.time_to_soc_update + >= TIME_TO_SOC_RECALCULATE_EVERY ) ): self.battery.time_to_soc_update = int(time()) diff --git a/etc/dbus-serialbattery/mnb.py b/etc/dbus-serialbattery/mnb.py index 8e91bd5b..ce627a39 100644 --- a/etc/dbus-serialbattery/mnb.py +++ b/etc/dbus-serialbattery/mnb.py @@ -3,7 +3,7 @@ from struct import * # from test_max17853 import *#{these two lines are mutually} -from util_max17853 import * # {exclusive. use test for testing} +# from util_max17853 import * # {exclusive. use test for testing} class MNBProtection(Protection): From aee71f0d364900bddeb724726cf7aac324b9d3a7 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 27 Apr 2023 21:53:58 +0200 Subject: [PATCH 119/209] fix renogy import --- etc/dbus-serialbattery/renogy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/renogy.py index ea513f23..64dee151 100644 --- a/etc/dbus-serialbattery/renogy.py +++ b/etc/dbus-serialbattery/renogy.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from battery import Battery, Cell -from utils import read_serial_data, unpack, unpack_from, logger +from utils import read_serial_data, unpack_from, logger import utils +from struct import unpack import struct From 9e358fa9c5b1549799b174cb34dff38a7e1748cb Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 08:04:25 +0200 Subject: [PATCH 120/209] added BMS info and cleanup * MNB * Revov * Sinowealth --- .flake8 | 5 ++--- etc/dbus-serialbattery/dbus-serialbattery.py | 1 + etc/dbus-serialbattery/mnb.py | 18 +++++++++++++----- .../{test_max17853.py => mnb_test_max17853.py} | 0 ...{util_max17853.py => mnb_utils_max17853.py} | 0 etc/dbus-serialbattery/revov.py | 5 ++++- etc/dbus-serialbattery/sinowealth.py | 4 ++++ 7 files changed, 24 insertions(+), 9 deletions(-) rename etc/dbus-serialbattery/{test_max17853.py => mnb_test_max17853.py} (100%) rename etc/dbus-serialbattery/{util_max17853.py => mnb_utils_max17853.py} (100%) diff --git a/.flake8 b/.flake8 index 85d1bd06..6dc18a5c 100644 --- a/.flake8 +++ b/.flake8 @@ -5,10 +5,9 @@ exclude = #./etc/dbus-serialbattery/dbus-serialbattery.py, ./etc/dbus-serialbattery/dbushelper.py, ./etc/dbus-serialbattery/minimalmodbus.py, - ./etc/dbus-serialbattery/mnb.py, + ./etc/dbus-serialbattery/mnb_test_max17853.py, + ./etc/dbus-serialbattery/mnb_utils_max17853.py, ./etc/dbus-serialbattery/revov.py, - ./etc/dbus-serialbattery/test_max17853.py, - ./etc/dbus-serialbattery/util_max17853.py, ./velib_python venv extend-ignore: diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 1fdd7bf1..363045f3 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -32,6 +32,7 @@ from renogy import Renogy from seplos import Seplos +# from mnb import MNB # from sinowealth import Sinowealth supported_bms_types = [ diff --git a/etc/dbus-serialbattery/mnb.py b/etc/dbus-serialbattery/mnb.py index ce627a39..a9d67613 100644 --- a/etc/dbus-serialbattery/mnb.py +++ b/etc/dbus-serialbattery/mnb.py @@ -1,9 +1,16 @@ # -*- coding: utf-8 -*- + +# disable MNB battery by default +# https://github.com/Louisvdw/dbus-serialbattery/commit/65241cbff36feb861ff43dbbcfb2b495f14a01ce +# remove duplicate MNB lines +# https://github.com/Louisvdw/dbus-serialbattery/commit/23afec33c2fd87fd4d4c53516f0a25f290643c82 + from battery import Protection, Battery, Cell -from struct import * +from utils import logger +from mnb_utils_max17853 import data_cycle, init_max -# from test_max17853 import *#{these two lines are mutually} -# from util_max17853 import * # {exclusive. use test for testing} +# from struct import * +# from mnb_test_max17853 import * # use test for testing class MNBProtection(Protection): @@ -90,8 +97,9 @@ def test_connection(self): result = False try: result = self.read_status_data() - except: - pass + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result diff --git a/etc/dbus-serialbattery/test_max17853.py b/etc/dbus-serialbattery/mnb_test_max17853.py similarity index 100% rename from etc/dbus-serialbattery/test_max17853.py rename to etc/dbus-serialbattery/mnb_test_max17853.py diff --git a/etc/dbus-serialbattery/util_max17853.py b/etc/dbus-serialbattery/mnb_utils_max17853.py similarity index 100% rename from etc/dbus-serialbattery/util_max17853.py rename to etc/dbus-serialbattery/mnb_utils_max17853.py diff --git a/etc/dbus-serialbattery/revov.py b/etc/dbus-serialbattery/revov.py index d3fc2563..b0eb65bf 100755 --- a/etc/dbus-serialbattery/revov.py +++ b/etc/dbus-serialbattery/revov.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- + +# Deprecate Revov driver - replaced by LifePower +# https://github.com/Louisvdw/dbus-serialbattery/pull/353/commits/c3ac9558fc86b386e5a6aefb313408165c86d240 + from battery import Protection, Battery, Cell from utils import * from struct import * import struct - # Author: L Sheed # Date: 3 May 2022 # Version 0.1.3 diff --git a/etc/dbus-serialbattery/sinowealth.py b/etc/dbus-serialbattery/sinowealth.py index 60b41895..263523fe 100755 --- a/etc/dbus-serialbattery/sinowealth.py +++ b/etc/dbus-serialbattery/sinowealth.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- + +# remove Sinowealth by default as it causes other issues but can be enabled manually +# https://github.com/Louisvdw/dbus-serialbattery/commit/7aab4c850a5c8d9c205efefc155fe62bb527da8e + from battery import Battery, Cell from utils import kelvin_to_celsius, read_serial_data, logger import utils From 09034a1327b7333f5f35487bf8c5c5a4bc3ddd08 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 08:18:46 +0200 Subject: [PATCH 121/209] moved BMS to subfolder --- etc/dbus-serialbattery/{ => bms}/ant.py | 0 .../{ => bms}/battery_template.py | 0 etc/dbus-serialbattery/{ => bms}/daly.py | 0 etc/dbus-serialbattery/{ => bms}/ecs.py | 0 .../{ => bms}/hlpdatabms4s.py | 0 .../{ => bms}/hlpdatabms4s_miniterm.py | 0 etc/dbus-serialbattery/{ => bms}/jkbms.py | 0 etc/dbus-serialbattery/{ => bms}/jkbms_ble.py | 2 +- etc/dbus-serialbattery/{ => bms}/jkbms_brn.py | 0 etc/dbus-serialbattery/{ => bms}/lifepower.py | 0 etc/dbus-serialbattery/{ => bms}/lltjbd.py | 0 etc/dbus-serialbattery/{ => bms}/mnb.py | 4 +-- .../{ => bms}/mnb_test_max17853.py | 0 .../{ => bms}/mnb_utils_max17853.py | 0 etc/dbus-serialbattery/{ => bms}/renogy.py | 0 etc/dbus-serialbattery/{ => bms}/revov.py | 0 etc/dbus-serialbattery/{ => bms}/seplos.py | 0 .../{ => bms}/sinowealth.py | 0 etc/dbus-serialbattery/dbus-serialbattery.py | 26 +++++++++---------- 19 files changed, 16 insertions(+), 16 deletions(-) rename etc/dbus-serialbattery/{ => bms}/ant.py (100%) rename etc/dbus-serialbattery/{ => bms}/battery_template.py (100%) rename etc/dbus-serialbattery/{ => bms}/daly.py (100%) rename etc/dbus-serialbattery/{ => bms}/ecs.py (100%) rename etc/dbus-serialbattery/{ => bms}/hlpdatabms4s.py (100%) rename etc/dbus-serialbattery/{ => bms}/hlpdatabms4s_miniterm.py (100%) rename etc/dbus-serialbattery/{ => bms}/jkbms.py (100%) rename etc/dbus-serialbattery/{ => bms}/jkbms_ble.py (99%) rename etc/dbus-serialbattery/{ => bms}/jkbms_brn.py (100%) rename etc/dbus-serialbattery/{ => bms}/lifepower.py (100%) rename etc/dbus-serialbattery/{ => bms}/lltjbd.py (100%) rename etc/dbus-serialbattery/{ => bms}/mnb.py (98%) rename etc/dbus-serialbattery/{ => bms}/mnb_test_max17853.py (100%) rename etc/dbus-serialbattery/{ => bms}/mnb_utils_max17853.py (100%) rename etc/dbus-serialbattery/{ => bms}/renogy.py (100%) rename etc/dbus-serialbattery/{ => bms}/revov.py (100%) rename etc/dbus-serialbattery/{ => bms}/seplos.py (100%) rename etc/dbus-serialbattery/{ => bms}/sinowealth.py (100%) diff --git a/etc/dbus-serialbattery/ant.py b/etc/dbus-serialbattery/bms/ant.py similarity index 100% rename from etc/dbus-serialbattery/ant.py rename to etc/dbus-serialbattery/bms/ant.py diff --git a/etc/dbus-serialbattery/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py similarity index 100% rename from etc/dbus-serialbattery/battery_template.py rename to etc/dbus-serialbattery/bms/battery_template.py diff --git a/etc/dbus-serialbattery/daly.py b/etc/dbus-serialbattery/bms/daly.py similarity index 100% rename from etc/dbus-serialbattery/daly.py rename to etc/dbus-serialbattery/bms/daly.py diff --git a/etc/dbus-serialbattery/ecs.py b/etc/dbus-serialbattery/bms/ecs.py similarity index 100% rename from etc/dbus-serialbattery/ecs.py rename to etc/dbus-serialbattery/bms/ecs.py diff --git a/etc/dbus-serialbattery/hlpdatabms4s.py b/etc/dbus-serialbattery/bms/hlpdatabms4s.py similarity index 100% rename from etc/dbus-serialbattery/hlpdatabms4s.py rename to etc/dbus-serialbattery/bms/hlpdatabms4s.py diff --git a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py b/etc/dbus-serialbattery/bms/hlpdatabms4s_miniterm.py similarity index 100% rename from etc/dbus-serialbattery/hlpdatabms4s_miniterm.py rename to etc/dbus-serialbattery/bms/hlpdatabms4s_miniterm.py diff --git a/etc/dbus-serialbattery/jkbms.py b/etc/dbus-serialbattery/bms/jkbms.py similarity index 100% rename from etc/dbus-serialbattery/jkbms.py rename to etc/dbus-serialbattery/bms/jkbms.py diff --git a/etc/dbus-serialbattery/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py similarity index 99% rename from etc/dbus-serialbattery/jkbms_ble.py rename to etc/dbus-serialbattery/bms/jkbms_ble.py index ecc0cf35..10d6bab7 100644 --- a/etc/dbus-serialbattery/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from battery import Battery, Cell from utils import logger -from jkbms_brn import JkBmsBle +from bms.jkbms_brn import JkBmsBle from bleak import BleakScanner, BleakError import asyncio import time diff --git a/etc/dbus-serialbattery/jkbms_brn.py b/etc/dbus-serialbattery/bms/jkbms_brn.py similarity index 100% rename from etc/dbus-serialbattery/jkbms_brn.py rename to etc/dbus-serialbattery/bms/jkbms_brn.py diff --git a/etc/dbus-serialbattery/lifepower.py b/etc/dbus-serialbattery/bms/lifepower.py similarity index 100% rename from etc/dbus-serialbattery/lifepower.py rename to etc/dbus-serialbattery/bms/lifepower.py diff --git a/etc/dbus-serialbattery/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py similarity index 100% rename from etc/dbus-serialbattery/lltjbd.py rename to etc/dbus-serialbattery/bms/lltjbd.py diff --git a/etc/dbus-serialbattery/mnb.py b/etc/dbus-serialbattery/bms/mnb.py similarity index 98% rename from etc/dbus-serialbattery/mnb.py rename to etc/dbus-serialbattery/bms/mnb.py index a9d67613..1ad1126e 100644 --- a/etc/dbus-serialbattery/mnb.py +++ b/etc/dbus-serialbattery/bms/mnb.py @@ -7,10 +7,10 @@ from battery import Protection, Battery, Cell from utils import logger -from mnb_utils_max17853 import data_cycle, init_max +from bms.mnb_utils_max17853 import data_cycle, init_max # from struct import * -# from mnb_test_max17853 import * # use test for testing +# from bms.mnb_test_max17853 import * # use test for testing class MNBProtection(Protection): diff --git a/etc/dbus-serialbattery/mnb_test_max17853.py b/etc/dbus-serialbattery/bms/mnb_test_max17853.py similarity index 100% rename from etc/dbus-serialbattery/mnb_test_max17853.py rename to etc/dbus-serialbattery/bms/mnb_test_max17853.py diff --git a/etc/dbus-serialbattery/mnb_utils_max17853.py b/etc/dbus-serialbattery/bms/mnb_utils_max17853.py similarity index 100% rename from etc/dbus-serialbattery/mnb_utils_max17853.py rename to etc/dbus-serialbattery/bms/mnb_utils_max17853.py diff --git a/etc/dbus-serialbattery/renogy.py b/etc/dbus-serialbattery/bms/renogy.py similarity index 100% rename from etc/dbus-serialbattery/renogy.py rename to etc/dbus-serialbattery/bms/renogy.py diff --git a/etc/dbus-serialbattery/revov.py b/etc/dbus-serialbattery/bms/revov.py similarity index 100% rename from etc/dbus-serialbattery/revov.py rename to etc/dbus-serialbattery/bms/revov.py diff --git a/etc/dbus-serialbattery/seplos.py b/etc/dbus-serialbattery/bms/seplos.py similarity index 100% rename from etc/dbus-serialbattery/seplos.py rename to etc/dbus-serialbattery/bms/seplos.py diff --git a/etc/dbus-serialbattery/sinowealth.py b/etc/dbus-serialbattery/bms/sinowealth.py similarity index 100% rename from etc/dbus-serialbattery/sinowealth.py rename to etc/dbus-serialbattery/bms/sinowealth.py diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 363045f3..383fce1a 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -22,18 +22,18 @@ from battery import Battery # import battery classes -from ant import Ant -from daly import Daly -from ecs import Ecs -from hlpdatabms4s import HLPdataBMS4S -from jkbms import Jkbms -from lifepower import Lifepower -from lltjbd import LltJbd -from renogy import Renogy -from seplos import Seplos - -# from mnb import MNB -# from sinowealth import Sinowealth +from bms.ant import Ant +from bms.daly import Daly +from bms.ecs import Ecs +from bms.hlpdatabms4s import HLPdataBMS4S +from bms.jkbms import Jkbms +from bms.lifepower import Lifepower +from bms.lltjbd import LltJbd +from bms.renogy import Renogy +from bms.seplos import Seplos + +# from bms.mnb import MNB +# from bms.sinowealth import Sinowealth supported_bms_types = [ {"bms": Ant, "baud": 19200}, @@ -108,7 +108,7 @@ def get_port() -> str: """ if port == "Jkbms_Ble": # noqa: F401 --> ignore flake "imported but unused" error - from jkbms_ble import Jkbms_Ble # noqa: F401 + from bms.jkbms_ble import Jkbms_Ble # noqa: F401 class_ = eval(port) testbms = class_("", 9600, sys.argv[2]) From a0918ee07c2205d82287ef5b29afe57ac8ac99ac Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 08:30:25 +0200 Subject: [PATCH 122/209] moved BMS to subfolder --- .flake8 | 8 ++++---- CHANGELOG.md | 3 ++- etc/dbus-serialbattery/dbus-serialbattery.py | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 6dc18a5c..52c0c3ad 100644 --- a/.flake8 +++ b/.flake8 @@ -1,13 +1,13 @@ [flake8] max-line-length = 120 exclude = - ./etc/dbus-serialbattery/battery_template.py, + ./etc/dbus-serialbattery/bms/battery_template.py, + ./etc/dbus-serialbattery/bms/mnb_test_max17853.py, + ./etc/dbus-serialbattery/bms/mnb_utils_max17853.py, + ./etc/dbus-serialbattery/bms/revov.py, #./etc/dbus-serialbattery/dbus-serialbattery.py, ./etc/dbus-serialbattery/dbushelper.py, ./etc/dbus-serialbattery/minimalmodbus.py, - ./etc/dbus-serialbattery/mnb_test_max17853.py, - ./etc/dbus-serialbattery/mnb_utils_max17853.py, - ./etc/dbus-serialbattery/revov.py, ./velib_python venv extend-ignore: diff --git a/CHANGELOG.md b/CHANGELOG.md index ba0d4aa5..51dd196a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,8 @@ * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel * Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek * Changed: Logging to get relevant data by @mr-manuel -* Changed: Moved ble part to `installble.sh` by @mr-manuel +* Changed: Moved Bluetooth part to `installble.sh` by @mr-manuel +* Changed: Moved BMS scripts to subfolder by @mr-manuel * Changed: Optimized installation scripts by @mr-manuel * Changed: Removed wildcard imports from several BMS drivers and fixed black lint errors by @mr-manuel * Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 by @mr-manuel diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 383fce1a..34e80d2f 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -47,6 +47,7 @@ {"bms": Renogy, "baud": 9600, "address": b"\x30"}, {"bms": Renogy, "baud": 9600, "address": b"\xF7"}, {"bms": Seplos, "baud": 19200}, + # {"bms": MNB, "baud": 9600}, # {"bms": Sinowealth}, ] expected_bms_types = [ From 8f7d705cd38fc4461b441db49951c082949ab21c Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 09:02:11 +0200 Subject: [PATCH 123/209] corrected installble to run correct script --- etc/dbus-serialbattery/installble.sh | 2 +- etc/dbus-serialbattery/reinstalllocal.sh | 2 ++ etc/dbus-serialbattery/utils.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index abe86010..63e028ea 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -29,7 +29,7 @@ install_service() { echo "#!/bin/sh" > /service/dbus-blebattery-$1/run echo "exec 2>&1" >> /service/dbus-blebattery-$1/run echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery-$1/run - echo "python /data/etc/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery-$1/run + echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery-$1/run chmod 755 /service/dbus-blebattery-$1/run } diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 6539e446..2b818b0a 100755 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -13,7 +13,9 @@ rm -rf /opt/victronenergy/service/$DRIVERNAME rm -rf /opt/victronenergy/service-templates/$DRIVERNAME rm -rf /opt/victronenergy/$DRIVERNAME mkdir /opt/victronenergy/$DRIVERNAME +mkdir /opt/victronenergy/$DRIVERNAME/bms cp -f /data/etc/$DRIVERNAME/* /opt/victronenergy/$DRIVERNAME &>/dev/null +cp -f /data/etc/$DRIVERNAME/bms/* /opt/victronenergy/$DRIVERNAME/bms &>/dev/null cp -rf /data/etc/$DRIVERNAME/service /opt/victronenergy/service-templates/$DRIVERNAME sh /data/etc/$DRIVERNAME/installqml.sh diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index d90bbfbb..8729e288 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230427)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230428)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 3a62eed2af61ee69b3a9c2507f07c428c258c2bd Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 09:43:58 +0200 Subject: [PATCH 124/209] Added self.unique_identifier to the battery class Used to identify a BMS when multiple BMS are connected planned for future use --- CHANGELOG.md | 1 + etc/dbus-serialbattery/battery.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51dd196a..8de3dac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Added: Balancing status for JKBMS by @mr-manuel * Added: Balancing switch status for JKBMS by @mr-manuel * Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel +* Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel * Added: Charge Mode display by @mr-manuel * Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel * Added: Config file by @ppuetsch diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index e86c7e2e..6b40846a 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -103,6 +103,9 @@ def __init__(self, port, baud, address): self.max_battery_charge_current = None self.max_battery_discharge_current = None + # used to identify a BMS when multiple BMS are connected - planned for future use + self.unique_identifier = None + @abstractmethod def test_connection(self) -> bool: """ From d88293714f30592d0579685a8a9f85a45b14189d Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 28 Apr 2023 16:19:49 +0200 Subject: [PATCH 125/209] changed ble service name from `dbus-blebattery-$1` to `dbus-blebattery.$1` like the non ble service --- etc/dbus-serialbattery/disabledriver.sh | 4 ++++ etc/dbus-serialbattery/installble.sh | 28 ++++++++++++++++--------- etc/dbus-serialbattery/uninstall.sh | 4 ++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh index ca81d230..33aa677f 100755 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disabledriver.sh @@ -10,6 +10,10 @@ sh /opt/victronenergy/swupdate-scripts/remount-rw.sh # remove files rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf +rm -rf /service/dbus-blebattery.* + +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +# can be removed on second release (>1.0.0) rm -rf /service/dbus-blebattery-* # kill if running diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 63e028ea..952d179b 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -20,17 +20,24 @@ if [ ! -f $filename ]; then fi grep -qxF "sh /data/etc/dbus-serialbattery/installble.sh" $filename || echo "sh /data/etc/dbus-serialbattery/installble.sh" >> $filename +# kill if running, needed when an adapter changes +pkill -f "python .*/dbus-serialbattery.py" + +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +# can be removed on second release (>1.0.0) +rm -rf /service/dbus-blebattery-* + install_service() { - mkdir -p /service/dbus-blebattery-$1/log - echo "#!/bin/sh" > /service/dbus-blebattery-$1/log/run - echo "exec multilog t s25000 n4 /var/log/dbus-blebattery-$1" >> /service/dbus-blebattery-$1/log/run - chmod 755 /service/dbus-blebattery-$1/log/run - - echo "#!/bin/sh" > /service/dbus-blebattery-$1/run - echo "exec 2>&1" >> /service/dbus-blebattery-$1/run - echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery-$1/run - echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery-$1/run - chmod 755 /service/dbus-blebattery-$1/run + mkdir -p /service/dbus-blebattery.$1/log + echo "#!/bin/sh" > /service/dbus-blebattery.$1/log/run + echo "exec multilog t s25000 n4 /var/log/dbus-blebattery.$1" >> /service/dbus-blebattery.$1/log/run + chmod 755 /service/dbus-blebattery.$1/log/run + + echo "#!/bin/sh" > /service/dbus-blebattery.$1/run + echo "exec 2>&1" >> /service/dbus-blebattery.$1/run + echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery.$1/run + echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery.$1/run + chmod 755 /service/dbus-blebattery.$1/run } @@ -38,5 +45,6 @@ install_service() { ## Uncomment for each adapter here, increase the number for each adapter/service +install_service 0 Jkbms_Ble C8:47:8C:E8:12:04 # install_service 0 Jkbms_Ble C8:47:8C:12:34:56 # install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index 64ff5467..5587acfc 100755 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -11,6 +11,10 @@ rm -f /data/conf/serial-starter.d/dbus-serialbattery.conf rm -rf /opt/victronenergy/service/dbus-serialbattery rm -rf /opt/victronenergy/service-templates/dbus-serialbattery rm -rf /opt/victronenergy/dbus-serialbattery +rm -rf /service/dbus-blebattery.* + +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +# can be removed on second release (>1.0.0) rm -rf /service/dbus-blebattery-* # kill if running From d3a72d61b4714ae4a52a1fa619234519b46be6af Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Fri, 28 Apr 2023 16:43:44 +0000 Subject: [PATCH 126/209] read installed capacity at startup --- etc/dbus-serialbattery/bms/daly.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index d91ae568..725ec4e8 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -31,6 +31,7 @@ def __init__(self, port, baud, address): command_temp = b"\x96" command_cell_balance = b"\x97" command_alarm = b"\x98" + command_rated_params = b"\x50" BATTERYTYPE = "Daly" LENGTH_CHECK = 1 LENGTH_POS = 3 @@ -56,6 +57,9 @@ def test_connection(self): def get_settings(self): self.capacity = utils.BATTERY_CAPACITY + with open_serial_port(self.port, self.baud_rate) as ser: + self.read_capacity(ser) + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT return True @@ -361,6 +365,20 @@ def read_fed_data(self, ser): self.capacity_remain = capacity_remain / 1000 return True + def read_capacity(self, ser): + capa_data = self.read_serial_data_daly(ser, self.command_rated_params) + # check if connection success + if capa_data is False: + logger.warning("read_capacity") + return False + + ( + capacity, + cell_volt + ) = unpack_from(">LL", capa_data) + self.capacity = capacity / 1000 + return True + def generate_command(self, command): buffer = bytearray(self.command_base) buffer[1] = self.command_address[0] # Always serial 40 or 80 From 9cea902fd5b6ad05a4a81dc17028dd17e8c9f6f4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Apr 2023 10:27:54 +0200 Subject: [PATCH 127/209] disable ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 --- CHANGELOG.md | 2 ++ etc/dbus-serialbattery/README.md | 5 ++++- etc/dbus-serialbattery/bms/ant.py | 4 ++++ etc/dbus-serialbattery/bms/sinowealth.py | 2 +- etc/dbus-serialbattery/dbus-serialbattery.py | 4 ++-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8de3dac8..ab19d959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Added: Config file by @ppuetsch * Added: Create empty `config.ini` for easier user usage by @mr-manuel * Added: Cronjob to restart Bluetooth service every 12 hours by @mr-manuel +* Added: Daly BMS read capacity https://github.com/Louisvdw/dbus-serialbattery/pull/594 by transistorgit * Added: Driver uninstall script by @mr-manuel * Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel * Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel @@ -41,6 +42,7 @@ * Changed TimeToSoc default value `TIME_TO_SOC_VALUE_TYPE` from `Both seconds and time string " [d h m s]"` to `1 Seconds` by @mr-manuel * Changed TimeToSoc description by @mr-manuel * Changed value positions, added groups and much clearer descriptions by @mr-manuel +* Changed: Disabled ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 by @mr-manuel diff --git a/etc/dbus-serialbattery/README.md b/etc/dbus-serialbattery/README.md index 127c1df8..de6c727e 100644 --- a/etc/dbus-serialbattery/README.md +++ b/etc/dbus-serialbattery/README.md @@ -1,2 +1,5 @@ # dbus-serialbattery -See [README on GitHub](https://github.com/Louisvdw/dbus-serialbattery/blob/master/README.md) \ No newline at end of file + +* See [README](https://github.com/Louisvdw/dbus-serialbattery/blob/master/README.md) on GitHub + +* See [Documentation](https://louisvdw.github.io/dbus-serialbattery/) on GitHub Pages diff --git a/etc/dbus-serialbattery/bms/ant.py b/etc/dbus-serialbattery/bms/ant.py index fde6d612..3558f012 100644 --- a/etc/dbus-serialbattery/bms/ant.py +++ b/etc/dbus-serialbattery/bms/ant.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- + +# disable Sinowealth by default as it causes other issues but can be enabled manually +# https://github.com/Louisvdw/dbus-serialbattery/issues/479 + from battery import Battery from utils import read_serial_data, logger import utils diff --git a/etc/dbus-serialbattery/bms/sinowealth.py b/etc/dbus-serialbattery/bms/sinowealth.py index 263523fe..7a9d9fdb 100755 --- a/etc/dbus-serialbattery/bms/sinowealth.py +++ b/etc/dbus-serialbattery/bms/sinowealth.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# remove Sinowealth by default as it causes other issues but can be enabled manually +# disable Sinowealth by default as it causes other issues but can be enabled manually # https://github.com/Louisvdw/dbus-serialbattery/commit/7aab4c850a5c8d9c205efefc155fe62bb527da8e from battery import Battery, Cell diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 34e80d2f..30c6eb52 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -22,7 +22,6 @@ from battery import Battery # import battery classes -from bms.ant import Ant from bms.daly import Daly from bms.ecs import Ecs from bms.hlpdatabms4s import HLPdataBMS4S @@ -32,11 +31,11 @@ from bms.renogy import Renogy from bms.seplos import Seplos +# from bms.ant import Ant # from bms.mnb import MNB # from bms.sinowealth import Sinowealth supported_bms_types = [ - {"bms": Ant, "baud": 19200}, {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Ecs, "baud": 19200}, @@ -47,6 +46,7 @@ {"bms": Renogy, "baud": 9600, "address": b"\x30"}, {"bms": Renogy, "baud": 9600, "address": b"\xF7"}, {"bms": Seplos, "baud": 19200}, + # {"bms": Ant, "baud": 19200}, # {"bms": MNB, "baud": 9600}, # {"bms": Sinowealth}, ] From 49694b67538263ba90b44a725a2ce57e31061861 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Sat, 29 Apr 2023 18:46:07 +0000 Subject: [PATCH 128/209] fix cell voltage header parser --- etc/dbus-serialbattery/bms/daly.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index d91ae568..6f982600 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -308,6 +308,7 @@ def read_cells_volts(self, ser): ) bufIdx += 13 # BBBBBhhhBB -> 13 byte else: + bufIdx += 1 # step through buffer to find valid start logger.warning("bad cell voltages header") return True From 3a583ed01eeec82289abbe19c51dccd9669f50a6 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Sat, 29 Apr 2023 18:59:59 +0000 Subject: [PATCH 129/209] rework daly receive routine --- etc/dbus-serialbattery/bms/daly.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 6f982600..262a1db8 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -374,13 +374,33 @@ def read_serial_data_daly(self, ser, command): ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK ) if data is False: + logger.info("No reply to cmd " + bytes(command).hex()) return False - start, flag, command_ret, length = unpack_from("BBBB", data) - checksum = sum(data[:-1]) & 0xFF + if len(data) <= 12: + logger.debug("Too short reply to cmd " + bytes(command).hex()) + return False; - if start == 165 and length == 8 and len(data) > 12 and checksum == data[12]: - return data[4 : length + 4] + # search sentence start + try: + idx = data.index(0xA5) + except ValueError: + logger.debug("No Sentence Start found for reply to cmd " + bytes(command).hex()) + return False + + if len(data[idx:]) <= 12: + logger.debug("Too short reply to cmd " + bytes(command).hex()) + return False; + + if data[12+idx] != sum(data[idx:12+idx]) & 0xFF: + logger.debug("Bad checksum in reply to cmd " + bytes(command).hex()) + return False + + _, _, _, length = unpack_from(">BBBB", data, idx) + + if length == 8: + return data[4 + idx : length + 4 + idx] else: - logger.error(">>> ERROR: Incorrect Reply") + logger.debug(">>> ERROR: Incorrect Reply to CMD " + bytes(command).hex() + ": 0x" + bytes(data).hex()) return False + From 554c01dbc234e377bad71b73c33b5f790e3c4ef1 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Sat, 29 Apr 2023 19:14:07 +0000 Subject: [PATCH 130/209] improve read cell voltages - only work on sufficient data, drop only the bad package on checksum error, not the complete transmission --- etc/dbus-serialbattery/bms/daly.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 262a1db8..1c452e3b 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -281,8 +281,8 @@ def read_cells_volts(self, ser): # logger.warning("data " + bytes(cells_volts_data).hex()) while ( - bufIdx < len(cells_volts_data) - 4 - ): # we at least need 4 bytes to extract the identifiers + bufIdx <= len(cells_volts_data) - (4 + 8 + 1) + ): # we at least need 13 bytes to extract the identifiers + 8 bytes payload + checksum b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: ( @@ -295,17 +295,17 @@ def read_cells_volts(self, ser): ) = unpack_from(">BhhhBB", cells_volts_data, bufIdx + 4) if sum(cells_volts_data[bufIdx : bufIdx + 12]) & 0xFF != chk: logger.warning("bad cell voltages checksum") - return False - for idx in range(3): - cellnum = ( - (frame - 1) * 3 - ) + idx # daly is 1 based, driver 0 based - if cellnum >= self.cell_count: - break - cellVoltage = frameCell[idx] / 1000 - self.cells[cellnum].voltage = ( - None if cellVoltage < lowMin else cellVoltage - ) + else: + for idx in range(3): + cellnum = ( + (frame - 1) * 3 + ) + idx # daly is 1 based, driver 0 based + if cellnum >= self.cell_count: + break + cellVoltage = frameCell[idx] / 1000 + self.cells[cellnum].voltage = ( + None if cellVoltage < lowMin else cellVoltage + ) bufIdx += 13 # BBBBBhhhBB -> 13 byte else: bufIdx += 1 # step through buffer to find valid start From e8ad26df8870793e9540c3d3dd2890482ae67c1a Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Sat, 29 Apr 2023 19:17:56 +0000 Subject: [PATCH 131/209] allow read_soc to also retry serial transmission --- etc/dbus-serialbattery/bms/daly.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 1c452e3b..e50b6db7 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -114,10 +114,11 @@ def read_soc_data(self, ser): crntMaxValid = utils.MAX_BATTERY_CHARGE_CURRENT * 1.3 triesValid = 2 while triesValid > 0: + triesValid -= 1 soc_data = self.read_serial_data_daly(ser, self.command_soc) # check if connection success if soc_data is False: - return False + continue voltage, tmp, current, soc = unpack_from(">hhhh", soc_data) current = ( @@ -132,8 +133,6 @@ def read_soc_data(self, ser): return True logger.warning("read_soc_data - triesValid " + str(triesValid)) - triesValid -= 1 - return False def read_alarm_data(self, ser): From 93a4e982150feafc845aeed67998b7671e97868f Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Sat, 29 Apr 2023 19:36:46 +0000 Subject: [PATCH 132/209] add daly cell balance state info. cells are red only if unbalanced now --- etc/dbus-serialbattery/bms/daly.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index e50b6db7..995c6a02 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -74,7 +74,7 @@ def refresh_data(self): result = result and self.read_temperature_range_data(ser) elif self.poll_step == 1: result = result and self.read_cells_volts(ser) - + result = result and self.read_balance_state(ser) # else: # A placeholder to remind this is the last step. Add any additional steps before here # This is last step so reset poll_step self.poll_step = -1 @@ -332,6 +332,20 @@ def read_cell_voltage_range_data(self, ser): self.cell_min_voltage = cell_min_voltage / 1000 return True + def read_balance_state(self, ser): + balance_data = self.read_serial_data_daly(ser, self.command_cell_balance) + # check if connection success + if balance_data is False: + logger.debug("read_balance_state") + return False + + bitdata = unpack_from(">Q", balance_data)[0] + + mask = 1 << 48 + for i in range(len(self.cells)): + self.cells[i].balance = True if bitdata & mask else False + mask >>= 1 + def read_temperature_range_data(self, ser): minmax_data = self.read_serial_data_daly(ser, self.command_minmax_temp) # check if connection success From fdd0bd1e757d36c852a7100a412464594177e495 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Apr 2023 23:09:03 +0200 Subject: [PATCH 133/209] bump version --- etc/dbus-serialbattery/bms/daly.py | 5 +---- etc/dbus-serialbattery/utils.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 725ec4e8..759a7556 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -372,10 +372,7 @@ def read_capacity(self, ser): logger.warning("read_capacity") return False - ( - capacity, - cell_volt - ) = unpack_from(">LL", capa_data) + (capacity, cell_volt) = unpack_from(">LL", capa_data) self.capacity = capacity / 1000 return True diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 8729e288..d81c33ab 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230428)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230429)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From ec0496914e71323a013dd0de45f8c89d8ce768d7 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 29 Apr 2023 23:14:24 +0200 Subject: [PATCH 134/209] typo --- etc/dbus-serialbattery/bms/ant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/ant.py b/etc/dbus-serialbattery/bms/ant.py index 3558f012..124f036f 100644 --- a/etc/dbus-serialbattery/bms/ant.py +++ b/etc/dbus-serialbattery/bms/ant.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# disable Sinowealth by default as it causes other issues but can be enabled manually +# disable ANT BMS by default as it causes other issues but can be enabled manually # https://github.com/Louisvdw/dbus-serialbattery/issues/479 from battery import Battery From d1238e07034547915691403bdb18520d6e88c4b6 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Mon, 1 May 2023 07:17:04 +0000 Subject: [PATCH 135/209] moved read_serialport_data() to daly.py --- etc/dbus-serialbattery/bms/daly.py | 84 ++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index d616da18..1fe469bc 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from battery import Battery, Cell -from utils import open_serial_port, read_serialport_data, logger +from utils import open_serial_port, logger import utils from struct import unpack_from +from time import sleep class Daly(Battery): @@ -263,7 +264,7 @@ def read_cells_volts(self, ser): maxFrame * 13 ) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + chksum - cells_volts_data = read_serialport_data( + cells_volts_data = self.read_serialport_data( ser, buffer, self.LENGTH_POS, 0, lenFixed ) if cells_volts_data is False: @@ -398,7 +399,7 @@ def generate_command(self, command): return buffer def read_serial_data_daly(self, ser, command): - data = read_serialport_data( + data = self.read_serialport_data( ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK ) if data is False: @@ -432,3 +433,80 @@ def read_serial_data_daly(self, ser, command): logger.debug(">>> ERROR: Incorrect Reply to CMD " + bytes(command).hex() + ": 0x" + bytes(data).hex()) return False + # Read data from previously openned serial port + def read_serialport_data( + self, + ser, + command, + length_pos, + length_check, + length_fixed=None, + length_size=None, + ): + try: + ser.flushOutput() + ser.flushInput() + ser.write(command) + + length_byte_size = 1 + if length_size is not None: + if length_size.upper() == "H": + length_byte_size = 2 + elif length_size.upper() == "I" or length_size.upper() == "L": + length_byte_size = 4 + + count = 0 + toread = ser.inWaiting() + + while toread < (length_pos + length_byte_size): + sleep(0.005) + toread = ser.inWaiting() + count += 1 + if count > 50: + logger.error(">>> ERROR: No reply - returning") + return False + + # logger.info('serial data toread ' + str(toread)) + res = ser.read(toread) + if length_fixed is not None: + length = length_fixed + else: + if len(res) < (length_pos + length_byte_size): + logger.error( + ">>> ERROR: No reply - returning [len:" + str(len(res)) + "]" + ) + return False + length_size = length_size if length_size is not None else "B" + length = unpack_from(">" + length_size, res, length_pos)[0] + + # logger.info('serial data length ' + str(length)) + + count = 0 + data = bytearray(res) + + packetlen = ( + length_fixed + if length_fixed is not None + else length_pos + length_byte_size + length + length_check + ) + while len(data) < packetlen: + res = ser.read(packetlen - len(data)) + data.extend(res) + # logger.info('serial data length ' + str(len(data))) + sleep(0.005) + count += 1 + if count > 150: + logger.error( + ">>> ERROR: No reply - returning [len:" + + str(len(data)) + + "/" + + str(length + length_check) + + "]" + ) + return False + + return data + + except Exception as e: + logger.error(e) + return False \ No newline at end of file From 62422dee5bdd8c3e21b8240efddf7a6609336bca Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Mon, 1 May 2023 07:29:13 +0000 Subject: [PATCH 136/209] revert read_serialport_data() to the state before my changes --- etc/dbus-serialbattery/utils.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index d81c33ab..c01c2568 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -392,7 +392,7 @@ def open_serial_port(port, baud): return ser -# Read data from previously openned serial port +# Read data from previously opened serial port def read_serialport_data( ser: serial.Serial, command, @@ -441,14 +441,8 @@ def read_serialport_data( count = 0 data = bytearray(res) - - packetlen = ( - length_fixed - if length_fixed is not None - else length_pos + length_byte_size + length + length_check - ) - while len(data) < packetlen: - res = ser.read(packetlen - len(data)) + while len(data) <= length + length_check: + res = ser.read(length + length_check) data.extend(res) # logger.info('serial data length ' + str(len(data))) sleep(0.005) From a3915cb61fe8bb30ef3c29b39ced9ac83ff5dea6 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Mon, 1 May 2023 07:36:54 +0000 Subject: [PATCH 137/209] fix connection log startup message. now voltage/current/soc are displayed correctly --- etc/dbus-serialbattery/bms/daly.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 1fe469bc..16fef5dd 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -48,6 +48,7 @@ def test_connection(self): ser = open_serial_port(self.port, self.baud_rate) if ser is not None: result = self.read_status_data(ser) + self.read_soc_data(ser) ser.close() except Exception as err: From 3153962aaa924065ad24ef2992545c8c0b3c53b6 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Mon, 1 May 2023 09:24:24 +0000 Subject: [PATCH 138/209] black reformatting --- etc/dbus-serialbattery/bms/daly.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 16fef5dd..917b1aa0 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -285,8 +285,8 @@ def read_cells_volts(self, ser): # logger.warning("data " + bytes(cells_volts_data).hex()) - while ( - bufIdx <= len(cells_volts_data) - (4 + 8 + 1) + while bufIdx <= len(cells_volts_data) - ( + 4 + 8 + 1 ): # we at least need 13 bytes to extract the identifiers + 8 bytes payload + checksum b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: @@ -313,7 +313,7 @@ def read_cells_volts(self, ser): ) bufIdx += 13 # BBBBBhhhBB -> 13 byte else: - bufIdx += 1 # step through buffer to find valid start + bufIdx += 1 # step through buffer to find valid start logger.warning("bad cell voltages header") return True @@ -409,20 +409,22 @@ def read_serial_data_daly(self, ser, command): if len(data) <= 12: logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False; + return False # search sentence start try: idx = data.index(0xA5) except ValueError: - logger.debug("No Sentence Start found for reply to cmd " + bytes(command).hex()) + logger.debug( + "No Sentence Start found for reply to cmd " + bytes(command).hex() + ) return False if len(data[idx:]) <= 12: logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False; + return False - if data[12+idx] != sum(data[idx:12+idx]) & 0xFF: + if data[12 + idx] != sum(data[idx : 12 + idx]) & 0xFF: logger.debug("Bad checksum in reply to cmd " + bytes(command).hex()) return False @@ -431,7 +433,12 @@ def read_serial_data_daly(self, ser, command): if length == 8: return data[4 + idx : length + 4 + idx] else: - logger.debug(">>> ERROR: Incorrect Reply to CMD " + bytes(command).hex() + ": 0x" + bytes(data).hex()) + logger.debug( + ">>> ERROR: Incorrect Reply to CMD " + + bytes(command).hex() + + ": 0x" + + bytes(data).hex() + ) return False # Read data from previously openned serial port @@ -510,4 +517,4 @@ def read_serialport_data( except Exception as e: logger.error(e) - return False \ No newline at end of file + return False From ec4a3347db64f379e25a7b0d8d4f650582fe002f Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 13:58:29 +0200 Subject: [PATCH 139/209] flake config change --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.flake8 b/.flake8 index 52c0c3ad..0e731225 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,7 @@ [flake8] max-line-length = 120 +per-file-ignores = + ./etc/dbus-serialbattery/utils.py: E501 exclude = ./etc/dbus-serialbattery/bms/battery_template.py, ./etc/dbus-serialbattery/bms/mnb_test_max17853.py, From 31a1e532a092e64471473f03a96c26ec4c489813 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 14:14:25 +0200 Subject: [PATCH 140/209] added linear voltage recalculation interval In the config file can now be defined how often CVL, CCL and DCL is recalculated --- etc/dbus-serialbattery/battery.py | 86 +++++++++++++++++------ etc/dbus-serialbattery/config.default.ini | 13 ++-- etc/dbus-serialbattery/utils.py | 18 +++-- 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 6b40846a..85965262 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -90,7 +90,9 @@ def __init__(self, port, baud, address): self.charge_mode = None self.charge_limitation = None self.discharge_limitation = None - self.control_voltage_last_set = 0 + self.linear_cvl_last_set = 0 + self.linear_ccl_last_set = 0 + self.linear_dcl_last_set = 0 self.max_voltage_start_time = None self.control_current = None self.control_previous_total = None @@ -353,22 +355,44 @@ def manage_charge_current(self) -> None: else: charge_limits_new.update({tmp: "SoC"}) - self.control_charge_current = round( - min(charge_limits), 3 - ) # gets changed after finished testing - - self.charge_limitation = ( - charge_limits_new[min(charge_limits_new)] - + " (" - + str(round(min(charge_limits_new), 3)) - + ")" + # do not set CCL immediately, but only + # - after LINEAR_RECALCULATION_EVERY passed + # - if CCL changes to 0 + # - if CCL changes more than LINEAR_RECALCULATION_ON_PERC_CHANGE + ccl = round(min(charge_limits), 3) # gets changed after finished testing + diff = ( + abs(self.control_charge_current - ccl) + if self.control_charge_current is not None + else 0 ) + if ( + int(time()) - self.linear_ccl_last_set >= utils.LINEAR_RECALCULATION_EVERY + or ccl == 0 + or ( + diff + >= self.control_charge_current + * utils.LINEAR_RECALCULATION_ON_PERC_CHANGE + / 100 + ) + ): + self.linear_ccl_last_set = int(time()) + + self.control_charge_current = ccl + + self.charge_limitation = ( + charge_limits_new[min(charge_limits_new)] + + " (" + + str(round(min(charge_limits_new), 3)) + + ")" + ) if self.control_charge_current == 0: self.control_allow_charge = False else: self.control_allow_charge = True + ##### + # Manage Discharge Current Limitations discharge_limits = [ self.max_battery_discharge_current @@ -388,10 +412,10 @@ def manage_charge_current(self) -> None: if self.max_battery_discharge_current != tmp: if tmp in discharge_limits_new: discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", Cell voltage"} + {tmp: discharge_limits_new[tmp] + ", Cell Voltage"} ) else: - discharge_limits_new.update({tmp: "Cell voltage"}) + discharge_limits_new.update({tmp: "Cell Voltage"}) if utils.DCCM_T_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToTemperature() @@ -425,16 +449,36 @@ def manage_charge_current(self) -> None: else: discharge_limits_new.update({tmp: "SoC"}) - self.control_discharge_current = round( - min(discharge_limits), 3 - ) # gets changed after finished testing - - self.discharge_limitation = ( - discharge_limits_new[min(discharge_limits_new)] - + " (" - + str(round(min(discharge_limits_new), 3)) - + ")" + # do not set DCL immediately, but only + # - after LINEAR_RECALCULATION_EVERY passed + # - if DCL changes to 0 + # - if DCL changes more than LINEAR_RECALCULATION_ON_PERC_CHANGE + dcl = round(min(discharge_limits), 3) # gets changed after finished testing + diff = ( + abs(self.control_discharge_current - dcl) + if self.control_discharge_current is not None + else 0 ) + if ( + int(time()) - self.linear_dcl_last_set >= utils.LINEAR_RECALCULATION_EVERY + or dcl == 0 + or ( + diff + >= self.control_discharge_current + * utils.LINEAR_RECALCULATION_ON_PERC_CHANGE + / 100 + ) + ): + self.linear_dcl_last_set = int(time()) + + self.control_discharge_current = dcl + + self.discharge_limitation = ( + discharge_limits_new[min(discharge_limits_new)] + + " (" + + str(round(min(discharge_limits_new), 3)) + + ")" + ) if self.control_discharge_current == 0: self.control_allow_discharge = False diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index b666c7d3..c559e7bc 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -1,15 +1,20 @@ [DEFAULT] +; Battery Current limits +MAX_BATTERY_CHARGE_CURRENT = 50.0 +MAX_BATTERY_DISCHARGE_CURRENT = 60.0 + ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) ; For CVL the penalties are only applied, if the cell voltage reaches the penalty voltage LINEAR_LIMITATION_ENABLE = False -; Battery Current limits -MAX_BATTERY_CHARGE_CURRENT = 50.0 -MAX_BATTERY_DISCHARGE_CURRENT = 60.0 - +; Specify in seconds how often the linear values should be recalculated +LINEAR_RECALCULATION_EVERY = 60 +; Specify in percent when the linear values should be recalculated immediately +; Example: 5 for a immediate change, when the value changes by more than 5% +LINEAR_RECALCULATION_ON_PERC_CHANGE = 5 ; --------- Charge Voltage limitation (affecting CVL) --------- ; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index c01c2568..b7503241 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,10 +36,16 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230429)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230501)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" +# Battery Current limits +MAX_BATTERY_CHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_CHARGE_CURRENT"]) +MAX_BATTERY_DISCHARGE_CURRENT = float( + config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"] +) + # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps # True is a linear mode. For CCL and DCL the values between the steps are calculated for @@ -48,10 +54,12 @@ def _get_list_from_config( # the penalty voltage LINEAR_LIMITATION_ENABLE = "True" == config["DEFAULT"]["LINEAR_LIMITATION_ENABLE"] -# Battery Current limits -MAX_BATTERY_CHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_CHARGE_CURRENT"]) -MAX_BATTERY_DISCHARGE_CURRENT = float( - config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"] +# Specify in seconds how often the penalty should be recalculated +LINEAR_RECALCULATION_EVERY = int(config["DEFAULT"]["LINEAR_RECALCULATION_EVERY"]) +# Specify in percent when the linear values should be recalculated immediately +# Example: 5 for a immediate change, when the value changes by more than 5% +LINEAR_RECALCULATION_ON_PERC_CHANGE = int( + config["DEFAULT"]["LINEAR_RECALCULATION_ON_PERC_CHANGE"] ) From 295efd103146a302e5dac437c8c0cf6ef943f11f Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 14:21:51 +0200 Subject: [PATCH 141/209] replaced penalty voltage calculation with automatically calculated penalty voltages to simplify config max voltage is kept until batteries are balanced --- CHANGELOG.md | 3 + etc/dbus-serialbattery/battery.py | 70 ++++++++++++++--------- etc/dbus-serialbattery/config.default.ini | 14 ++--- etc/dbus-serialbattery/utils.py | 46 +++++++-------- 4 files changed, 74 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab19d959..b7019cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,11 @@ * Added: JKBMS BLE - MOS temperature by @mr-manuel * Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel * Added: Post install notes by @mr-manuel +* Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel * Added: Script to install directly from repository by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel +* Added: Show MOSFET temperature for JKBMS https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @baphomett * Added: Show specific TimeToSoC points in GUI, if 0%, 10%, 20%, 80%, 90% and/or 100% are selected by @mr-manuel * Added: Show TimeToGo in GUI only, if enabled by @mr-manuel * Added: Support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 by @peterohman @@ -56,6 +58,7 @@ * Changed: Moved Bluetooth part to `installble.sh` by @mr-manuel * Changed: Moved BMS scripts to subfolder by @mr-manuel * Changed: Optimized installation scripts by @mr-manuel +* Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel * Changed: Removed wildcard imports from several BMS drivers and fixed black lint errors by @mr-manuel * Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 by @mr-manuel * Changed: Separate Time-To-Go and Time-To-SoC activation by @mr-manuel diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 85965262..2aa3c317 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -183,41 +183,45 @@ def manage_charge_voltage_linear(self) -> None: if voltage: voltageSum += voltage - # calculate penalty sum to prevent single cell overcharge - if voltage >= utils.PENALTY_AT_CELL_VOLTAGE[0]: + # calculate penalty sum to prevent single cell overcharge by using current cell voltage + if voltage > utils.MAX_CELL_VOLTAGE: # foundHighCellVoltage: reset to False is not needed, since it is recalculated every second foundHighCellVoltage = True - penaltySum += utils.calcLinearRelationship( - voltage, - utils.PENALTY_AT_CELL_VOLTAGE, - utils.PENALTY_BATTERY_VOLTAGE, - ) + penaltySum += voltage - utils.MAX_CELL_VOLTAGE - 0.010 - voltageSum = round(voltageSum, 3) + voltageDiff = self.get_max_cell_voltage() - self.get_min_cell_voltage() if self.max_voltage_start_time is None: if ( utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + and voltageDiff <= utils.CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL and self.allow_max_voltage ): self.max_voltage_start_time = time() elif ( - utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + # utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT and not self.allow_max_voltage ): self.allow_max_voltage = True else: tDiff = time() - self.max_voltage_start_time - if utils.MAX_VOLTAGE_TIME_SEC < tDiff: + # if utils.MAX_VOLTAGE_TIME_SEC < tDiff: + # keep max voltage for 300 more seconds + if 300 < tDiff: self.allow_max_voltage = False self.max_voltage_start_time = None # INFO: battery will only switch to Absorption, if all cells are balanced. # Reach MAX_CELL_VOLTAGE * cell count if they are all balanced. if foundHighCellVoltage and self.allow_max_voltage: - # set CVL only once every PENALTY_RECALCULATE_EVERY seconds - control_voltage_time = int(time() / utils.PENALTY_RECALCULATE_EVERY) - if control_voltage_time != self.control_voltage_last_set: + # set CVL only once every LINEAR_RECALCULATION_EVERY seconds + if ( + int(time()) - self.linear_cvl_last_set + >= utils.LINEAR_RECALCULATION_EVERY + ): + self.linear_cvl_last_set = int(time()) + # Keep penalty above min battery voltage self.control_voltage = round( max( @@ -226,26 +230,40 @@ def manage_charge_voltage_linear(self) -> None: ), 3, ) - self.control_voltage_last_set = control_voltage_time + self.charge_mode = ( - "Bulk dynamic (linear mode)" + "Bulk dynamic (vS: " + + str(round(voltageSum, 2)) + + " - pS: " + + str(round(penaltySum, 2)) + + ")" if self.max_voltage_start_time is None - else "Absorption dynamic (linear mode)" + else "Absorption dynamic (vS: " + + str(round(voltageSum, 2)) + + " - pS: " + + str(round(penaltySum, 2)) + + ")" ) elif self.allow_max_voltage: self.control_voltage = round((utils.MAX_CELL_VOLTAGE * self.cell_count), 3) self.charge_mode = ( - "Bulk (linear mode)" - if self.max_voltage_start_time is None - else "Absorption (linear mode)" + "Bulk" if self.max_voltage_start_time is None else "Absorption" ) else: self.control_voltage = round( (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3 ) - self.charge_mode = "Float (linear mode)" + self.charge_mode = "Float" + + if ( + self.allow_max_voltage + and self.get_balancing() + and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT + ): + self.charge_mode += " + Balancing" + self.charge_mode += " (LM)" def manage_charge_voltage_step(self) -> None: """ @@ -295,14 +313,14 @@ def manage_charge_voltage_step(self) -> None: if self.allow_max_voltage: self.control_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.charge_mode = ( - "Bulk (step mode)" - if self.max_voltage_start_time is None - else "Absorption (step mode)" + "Bulk" if self.max_voltage_start_time is None else "Absorption" ) else: self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count - self.charge_mode = "Float (step mode)" + self.charge_mode = "Float" + + self.charge_mode += " (Step Mode)" def manage_charge_current(self) -> None: # Manage Charge Current Limitations @@ -322,10 +340,10 @@ def manage_charge_current(self) -> None: if self.max_battery_charge_current != tmp: if tmp in charge_limits_new: charge_limits_new.update( - {tmp: charge_limits_new[tmp] + ", Cell voltage"} + {tmp: charge_limits_new[tmp] + ", Cell Voltage"} ) else: - charge_limits_new.update({tmp: "Cell voltage"}) + charge_limits_new.update({tmp: "Cell Voltage"}) if utils.CCCM_T_ENABLE: tmp = self.calcMaxChargeCurrentReferringToTemperature() diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index c559e7bc..444afaec 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -33,6 +33,7 @@ MIN_CELL_VOLTAGE = 2.90 MAX_CELL_VOLTAGE = 3.45 FLOAT_CELL_VOLTAGE = 3.35 +; ----- DEPRECATED | start ; -- Penalty Voltages ; NOTE: works only when LINEAR_LIMITATION_ENABLE = True ; More details can be found here: https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635 @@ -41,16 +42,15 @@ FLOAT_CELL_VOLTAGE = 3.35 ; There will be a sum of all penalties for each cell, which exceeds the limits ; NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of PENALTY_BATTERY_VOLTAGE, ; else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. -PENALTY_AT_CELL_VOLTAGE = 3.48, 3.55, 3.6 +; PENALTY_AT_CELL_VOLTAGE = 3.48, 3.55, 3.6 ; this voltage will be subtracted -PENALTY_BATTERY_VOLTAGE = 0.01, 1.0, 2.0 -; Specify in seconds how often the penalty should be recalculated -PENALTY_RECALCULATE_EVERY = 60 +; PENALTY_BATTERY_VOLTAGE = 0.01, 1.0, 2.0 +; ----- DEPRECATED | end -; -- CVL Reset based on SoC option -; Reset max voltage after +; -- CVL reset based on SoC option (step mode) +; Specify how long the max voltage should be kept if reached, then switch to float voltage MAX_VOLTAGE_TIME_SEC = 900 -; Specify SoC where CVL limit is reset to max voltage +; Specify SoC where CVL limit is reset to max voltage, if value gets below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90 diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index b7503241..0763852e 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -48,10 +48,8 @@ def _get_list_from_config( # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps -# True is a linear mode. For CCL and DCL the values between the steps are calculated for -# smoother values (by WaldemarFech) -# For CVL the penalties are only applied, if the cell voltage reaches -# the penalty voltage +# True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) +# For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded LINEAR_LIMITATION_ENABLE = "True" == config["DEFAULT"]["LINEAR_LIMITATION_ENABLE"] # Specify in seconds how often the penalty should be recalculated @@ -64,12 +62,17 @@ def _get_list_from_config( # --------- Charge Voltage limitation (affecting CVL) --------- -# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to -# float voltage (FLOAT_CELL_VOLTAGE * cell count) after max voltage is reached for MAX_VOLTAGE_TIME_SEC. -# It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT. -# If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied -# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float -# voltage of 53.6V to don't stress the batteries. Allow max voltage of 55.2V again, if SoC is once below 90% +# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) and back +# Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it +# switches back to max voltage. +# Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to +# float voltage after 300 (fixed) additional seconds. After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT +# it switches back to max voltage. +# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. +# Allow max voltage of 55.2V again, if SoC is once below 90% +# OR +# The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float voltage of 53.6V after 300 additional seconds +# to don't stress the batteries. Allow max voltage of 55.2V again if max cell difference is above 0.050V # Charge voltage control management enable (True/False). CVCM_ENABLE = "True" == config["DEFAULT"]["CVCM_ENABLE"] @@ -81,24 +84,15 @@ def _get_list_from_config( # Max voltage can seen as absorption voltage FLOAT_CELL_VOLTAGE = float(config["DEFAULT"]["FLOAT_CELL_VOLTAGE"]) -# -- Penalty Voltages -# NOTE: works only when LINEAR_LIMITATION_ENABLE = True -# More details can be found here: https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635 -# If the cell voltage reaches 3.48V, then reduce actual battery-voltage by 0.01V -# If the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded -# There will be a sum of all penalties for each cell, which exceeds the limits -# NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of -# PENALTY_BATTERY_VOLTAGE, else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage -# has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. -PENALTY_AT_CELL_VOLTAGE = _get_list_from_config( - "DEFAULT", "PENALTY_AT_CELL_VOLTAGE", lambda v: float(v) +# -- CVL reset based on cell voltage diff (linear mode) +# Specify cell voltage diff where CVL limit is kept until diff is equal or lower +CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = float( + config["DEFAULT"]["CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL"] ) -# this voltage will be subtracted -PENALTY_BATTERY_VOLTAGE = _get_list_from_config( - "DEFAULT", "PENALTY_BATTERY_VOLTAGE", lambda v: float(v) +# Specify cell voltage diff where CVL limit is reset to max voltage, if value get above +CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = float( + config["DEFAULT"]["CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT"] ) -# Specify in seconds how often the penalty should be recalculated -PENALTY_RECALCULATE_EVERY = int(config["DEFAULT"]["PENALTY_RECALCULATE_EVERY"]) # -- CVL Reset based on SoC option # Reset max voltage after From f95dc05781f0521182d75e7598479eebf6c6c5ca Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 14:24:03 +0200 Subject: [PATCH 142/209] fix black lint errors --- etc/dbus-serialbattery/bms/daly.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 16fef5dd..917b1aa0 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -285,8 +285,8 @@ def read_cells_volts(self, ser): # logger.warning("data " + bytes(cells_volts_data).hex()) - while ( - bufIdx <= len(cells_volts_data) - (4 + 8 + 1) + while bufIdx <= len(cells_volts_data) - ( + 4 + 8 + 1 ): # we at least need 13 bytes to extract the identifiers + 8 bytes payload + checksum b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: @@ -313,7 +313,7 @@ def read_cells_volts(self, ser): ) bufIdx += 13 # BBBBBhhhBB -> 13 byte else: - bufIdx += 1 # step through buffer to find valid start + bufIdx += 1 # step through buffer to find valid start logger.warning("bad cell voltages header") return True @@ -409,20 +409,22 @@ def read_serial_data_daly(self, ser, command): if len(data) <= 12: logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False; + return False # search sentence start try: idx = data.index(0xA5) except ValueError: - logger.debug("No Sentence Start found for reply to cmd " + bytes(command).hex()) + logger.debug( + "No Sentence Start found for reply to cmd " + bytes(command).hex() + ) return False if len(data[idx:]) <= 12: logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False; + return False - if data[12+idx] != sum(data[idx:12+idx]) & 0xFF: + if data[12 + idx] != sum(data[idx : 12 + idx]) & 0xFF: logger.debug("Bad checksum in reply to cmd " + bytes(command).hex()) return False @@ -431,7 +433,12 @@ def read_serial_data_daly(self, ser, command): if length == 8: return data[4 + idx : length + 4 + idx] else: - logger.debug(">>> ERROR: Incorrect Reply to CMD " + bytes(command).hex() + ": 0x" + bytes(data).hex()) + logger.debug( + ">>> ERROR: Incorrect Reply to CMD " + + bytes(command).hex() + + ": 0x" + + bytes(data).hex() + ) return False # Read data from previously openned serial port @@ -510,4 +517,4 @@ def read_serialport_data( except Exception as e: logger.error(e) - return False \ No newline at end of file + return False From 52fdc4a78843b23cfa73acd4513ceb1bc6eb8340 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 14:31:30 +0200 Subject: [PATCH 143/209] updated config.default.ini --- etc/dbus-serialbattery/config.default.ini | 37 +++++++++++------------ etc/dbus-serialbattery/dbushelper.py | 6 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 444afaec..078d833f 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -7,7 +7,7 @@ MAX_BATTERY_DISCHARGE_CURRENT = 60.0 ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) -; For CVL the penalties are only applied, if the cell voltage reaches the penalty voltage +; For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded LINEAR_LIMITATION_ENABLE = False ; Specify in seconds how often the linear values should be recalculated @@ -16,14 +16,21 @@ LINEAR_RECALCULATION_EVERY = 60 ; Example: 5 for a immediate change, when the value changes by more than 5% LINEAR_RECALCULATION_ON_PERC_CHANGE = 5 + ; --------- Charge Voltage limitation (affecting CVL) --------- -; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count) and switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) -; after max voltage is reached for MAX_VOLTAGE_TIME_SEC. It switches back to max voltage after SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT -; If LINEAR_LIMITATION_ENABLE is set to True then penalty voltages are applied +; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) and back +; Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it +; switches back to max voltage. +; Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to +; float voltage after 300 (fixed) additional seconds. After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT +; it switches back to max voltage. ; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. ; Allow max voltage of 55.2V again, if SoC is once below 90% +; OR +; The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float voltage of 53.6V after 300 additional seconds +; to don't stress the batteries. Allow max voltage of 55.2V again if max cell difference is above 0.050V ; Charge voltage control management enable (True/False). -CVCM_ENABLE = False +CVCM_ENABLE = True ; -- Cell Voltages ; Description: Cell min/max voltages which are used to calculate the min/max battery voltage @@ -33,22 +40,14 @@ MIN_CELL_VOLTAGE = 2.90 MAX_CELL_VOLTAGE = 3.45 FLOAT_CELL_VOLTAGE = 3.35 -; ----- DEPRECATED | start -; -- Penalty Voltages -; NOTE: works only when LINEAR_LIMITATION_ENABLE = True -; More details can be found here: https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635 -; If the cell voltage reaches 3.48V, then reduce actual battery-voltage by 0.01V -; If the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded -; There will be a sum of all penalties for each cell, which exceeds the limits -; NOTE: The first value of PENALTY_AT_CELL_VOLTAGE has to be at least MAX_CELL_VOLTAGE + the first value of PENALTY_BATTERY_VOLTAGE, -; else the FLOAT_CELL_VOLTAGE is never set. Additionally the battery voltage has to reach max voltage and all cells has to be below penalty voltage to switch to float voltage. -; PENALTY_AT_CELL_VOLTAGE = 3.48, 3.55, 3.6 -; this voltage will be subtracted -; PENALTY_BATTERY_VOLTAGE = 0.01, 1.0, 2.0 -; ----- DEPRECATED | end +; -- CVL reset based on cell voltage diff (linear mode) +; Specify cell voltage diff where CVL limit is kept until diff is equal or lower +CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = 0.010 +; Specify cell voltage diff where CVL limit is reset to max voltage, if value get above +CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = 0.050 ; -- CVL reset based on SoC option (step mode) -; Specify how long the max voltage should be kept if reached, then switch to float voltage +; Specify how long the max voltage should be kept, if reached then switch to float voltage MAX_VOLTAGE_TIME_SEC = 900 ; Specify SoC where CVL limit is reset to max voltage, if value gets below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90 diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 246a1aad..9ecd7229 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -336,12 +336,12 @@ def publish_battery(self, loop): if self.error_count >= 60: loop.quit() - # This is to mannage CCL\DCL - self.battery.manage_charge_current() - # This is to mannage CVCL self.battery.manage_charge_voltage() + # This is to mannage CCL\DCL + self.battery.manage_charge_current() + # publish all the data from the battery object to dbus self.publish_dbus() From 912cd74d9ef72a0568bd176601c732b41a84660e Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 16:23:26 +0200 Subject: [PATCH 144/209] update nightly install script --- etc/dbus-serialbattery/install-nightly.sh | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index f59857ac..f60954c1 100755 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -33,18 +33,33 @@ cd /tmp # clean already extracted folder rm -rf /tmp/dbus-serialbattery-$branch +# download driver wget -O $branch.zip https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip -unzip $branch.zip +# extract archive +unzip -q $branch.zip + +# backup config.ini +if [ -f "/data/etc/dbus-serialbattery/config.ini" ]; then + mv /data/etc/dbus-serialbattery/config.ini /data/etc/config.ini +fi + +# remove old driver +rm -rf /data/etc/dbus-serialbattery + +# copy driver cp -rf /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc +# restore config.ini +if [ -f "/data/etc/config.ini" ]; then + mv /data/etc/config.ini /data/etc/dbus-serialbattery/config.ini +fi + +# set permissions chmod +x /data/etc/dbus-serialbattery/*.sh chmod +x /data/etc/dbus-serialbattery/*.py chmod +x /data/etc/dbus-serialbattery/service/run chmod +x /data/etc/dbus-serialbattery/service/log/run +# run install script bash /data/etc/dbus-serialbattery/reinstalllocal.sh - -#if [[ $branch == "jkbms_ble" ]]; then -# nano /data/etc/dbus-serialbattery/installble.sh -#fi From 93caa2358875065e8242ec6f17c89a368e379fb2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 16:26:57 +0200 Subject: [PATCH 145/209] Removed line --- etc/dbus-serialbattery/installble.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh index 952d179b..207607c4 100755 --- a/etc/dbus-serialbattery/installble.sh +++ b/etc/dbus-serialbattery/installble.sh @@ -45,6 +45,5 @@ install_service() { ## Uncomment for each adapter here, increase the number for each adapter/service -install_service 0 Jkbms_Ble C8:47:8C:E8:12:04 # install_service 0 Jkbms_Ble C8:47:8C:12:34:56 # install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC From fce00b998a929fcf7d0b75b2f4b11de4823d366a Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 1 May 2023 18:31:56 +0200 Subject: [PATCH 146/209] fixed error in HLPdataBMS4S --- etc/dbus-serialbattery/bms/hlpdatabms4s.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/bms/hlpdatabms4s.py b/etc/dbus-serialbattery/bms/hlpdatabms4s.py index 6fcbc6b0..3f99aa8e 100644 --- a/etc/dbus-serialbattery/bms/hlpdatabms4s.py +++ b/etc/dbus-serialbattery/bms/hlpdatabms4s.py @@ -7,8 +7,8 @@ class HLPdataBMS4S(Battery): - def __init__(self, port, baud): - super(HLPdataBMS4S, self).__init__(port, baud) + def __init__(self, port, baud, address): + super(HLPdataBMS4S, self).__init__(port, baud, address) self.type = self.BATTERYTYPE BATTERYTYPE = "HLPdataBMS4S" From 9e4b069d8b39971ddfedc1e60a1f3774e36971c4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 08:26:02 +0200 Subject: [PATCH 147/209] fixed wrong variable assignment `str` instead of `int` --- etc/dbus-serialbattery/battery.py | 4 ++-- etc/dbus-serialbattery/bms/jkbms.py | 11 ++++++----- etc/dbus-serialbattery/bms/jkbms_ble.py | 2 +- etc/dbus-serialbattery/bms/lltjbd.py | 6 ++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 2aa3c317..1a45cde1 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -150,12 +150,12 @@ def to_temp(self, sensor: int, value: float) -> None: :param value: the sensor value :return: """ + if sensor == 0: + self.temp_mos = min(max(value, -20), 100) if sensor == 1: self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) - if sensor == "mos": - self.temp_mos = min(max(value, -20), 100) def manage_charge_voltage(self) -> None: """ diff --git a/etc/dbus-serialbattery/bms/jkbms.py b/etc/dbus-serialbattery/bms/jkbms.py index fd3e12bc..54da2c8a 100644 --- a/etc/dbus-serialbattery/bms/jkbms.py +++ b/etc/dbus-serialbattery/bms/jkbms.py @@ -84,6 +84,12 @@ def read_status_data(self): unpack_from(">xH", celldata, c * 3 + 1)[0] / 1000 ) + # MOSFET temperature + offset = cellbyte_count + 3 + temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] + self.to_temp(0, temp_mos if temp_mos < 99 else (100 - temp_mos)) + + # Temperature sensors offset = cellbyte_count + 6 temp1 = unpack_from(">H", self.get_data(status_data, b"\x81", offset, 2))[0] offset = cellbyte_count + 9 @@ -91,11 +97,6 @@ def read_status_data(self): self.to_temp(1, temp1 if temp1 < 99 else (100 - temp1)) self.to_temp(2, temp2 if temp2 < 99 else (100 - temp2)) - # MOSFET temperature - offset = cellbyte_count + 3 - temp_mos = unpack_from(">H", self.get_data(status_data, b"\x80", offset, 2))[0] - self.to_temp("mos", temp_mos if temp_mos < 99 else (100 - temp_mos)) - offset = cellbyte_count + 12 voltage = unpack_from(">H", self.get_data(status_data, b"\x83", offset, 2))[0] self.voltage = voltage / 100 diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py index 10d6bab7..45bfdd35 100644 --- a/etc/dbus-serialbattery/bms/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -147,9 +147,9 @@ def refresh_data(self): for c in range(self.cell_count): self.cells[c].voltage = st["cell_info"]["voltages"][c] + self.to_temp(0, st["cell_info"]["temperature_mos"]) self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.to_temp("mos", st["cell_info"]["temperature_mos"]) self.current = st["cell_info"]["current"] self.voltage = st["cell_info"]["total_voltage"] diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index c285d44a..09486e68 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -165,12 +165,10 @@ def read_gen_data(self): self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count + # 0 = MOS, 1 = temp 1, 2 = temp 2 for t in range(self.temp_sensors): temp1 = unpack_from(">H", gen_data, 23 + (2 * t))[0] - if t == 0: - self.to_temp("mos", utils.kelvin_to_celsius(temp1 / 10)) - else: - self.to_temp(t, utils.kelvin_to_celsius(temp1 / 10)) + self.to_temp(t, utils.kelvin_to_celsius(temp1 / 10)) return True From 80e2c67c7b61c0bc80e8d163e97a79bbc47d797d Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 14:05:24 +0200 Subject: [PATCH 148/209] updated battery template --- .../bms/battery_template.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/etc/dbus-serialbattery/bms/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py index 10b851ee..4b308ab7 100644 --- a/etc/dbus-serialbattery/bms/battery_template.py +++ b/etc/dbus-serialbattery/bms/battery_template.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- + +# NOTES +# Please also update the feature comparison table, if you are adding a new BMS +# https://louisvdw.github.io/dbus-serialbattery/general/features/#bms-feature-comparison + from battery import Protection, Battery, Cell from utils import is_bit_set, read_serial_data, logger import utils @@ -32,12 +37,23 @@ def get_settings(self): # Set the current limits, populate cell count, etc # Return True if success, False for failure - # Uncomment if BMS does not supply capacity - # self.capacity = BATTERY_CAPACITY - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.capacity = ( + utils.BATTERY_CAPACITY # if possible replace constant with value read from BMS + ) + self.max_battery_charge_current = ( + utils.MAX_BATTERY_CHARGE_CURRENT # if possible replace constant with value read from BMS + ) + self.max_battery_discharge_current = ( + utils.MAX_BATTERY_DISCHARGE_CURRENT # if possible replace constant with value read from BMS + ) self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count + + # provide a unique identifier from the BMS to identify a BMS, if multiple same BMS are connected + # e.g. the serial number + # If there is no such value, please leave the line commented. In this case the capacity is used, + # since it can be changed by small amounts to make a battery unique. On +/- 5 Ah you can identify 11 batteries + # self.unique_identifier = str() return True def refresh_data(self): From 70658e972e1b0d2497dedd38fb33e664ceea82e8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:05:10 +0200 Subject: [PATCH 149/209] Fix for #450 https://github.com/Louisvdw/dbus-serialbattery/issues/450 --- etc/dbus-serialbattery/bms/ecs.py | 3 +++ etc/dbus-serialbattery/bms/jkbms_ble.py | 9 +++++++-- etc/dbus-serialbattery/bms/lltjbd.py | 3 +++ etc/dbus-serialbattery/bms/renogy.py | 3 +++ etc/dbus-serialbattery/bms/revov.py | 8 ++++++-- etc/dbus-serialbattery/utils.py | 2 +- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/bms/ecs.py b/etc/dbus-serialbattery/bms/ecs.py index 41065b7e..a50e6072 100644 --- a/etc/dbus-serialbattery/bms/ecs.py +++ b/etc/dbus-serialbattery/bms/ecs.py @@ -46,6 +46,9 @@ def test_connection(self): self.find_LiPro_cells() + # get first data to show in startup log + self.refresh_data() + return self.get_settings() except IOError: return False diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py index 45bfdd35..b495aa8e 100644 --- a/etc/dbus-serialbattery/bms/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -91,6 +91,11 @@ def test_connection(self): return False logger.info("JK BMS found!") + + # get first data to show in startup log + self.get_settings() + self.refresh_data() + return True def get_settings(self): @@ -150,8 +155,8 @@ def refresh_data(self): self.to_temp(0, st["cell_info"]["temperature_mos"]) self.to_temp(1, st["cell_info"]["temperature_sensor_1"]) self.to_temp(2, st["cell_info"]["temperature_sensor_2"]) - self.current = st["cell_info"]["current"] - self.voltage = st["cell_info"]["total_voltage"] + self.current = round(st["cell_info"]["current"], 1) + self.voltage = round(st["cell_info"]["total_voltage"], 2) self.soc = st["cell_info"]["battery_soc"] self.cycles = st["cell_info"]["cycle_count"] diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 09486e68..9bfcd7a5 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -67,6 +67,9 @@ def test_connection(self): result = False try: result = self.read_hardware_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False diff --git a/etc/dbus-serialbattery/bms/renogy.py b/etc/dbus-serialbattery/bms/renogy.py index 64dee151..acfe2335 100644 --- a/etc/dbus-serialbattery/bms/renogy.py +++ b/etc/dbus-serialbattery/bms/renogy.py @@ -47,6 +47,9 @@ def test_connection(self): result = False try: result = self.read_gen_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False diff --git a/etc/dbus-serialbattery/bms/revov.py b/etc/dbus-serialbattery/bms/revov.py index b0eb65bf..17764989 100755 --- a/etc/dbus-serialbattery/bms/revov.py +++ b/etc/dbus-serialbattery/bms/revov.py @@ -54,8 +54,12 @@ def test_connection(self): result = False try: result = self.read_gen_data() - except: - pass + # get first data to show in startup log + if result: + self.refresh_data() + except Exception as err: + logger.error(f"Unexpected {err=}, {type(err)=}") + result = False return result diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 0763852e..4037fbbf 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230501)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230502)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From fbfdcc711498dcbd4e420bc32634f527c409da2e Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:07:15 +0200 Subject: [PATCH 150/209] Read charge/discharge limit JKBMS https://github.com/Louisvdw/dbus-serialbattery/issues/4 --- etc/dbus-serialbattery/battery.py | 8 ++++++++ etc/dbus-serialbattery/bms/jkbms.py | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 1a45cde1..355e069e 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -900,6 +900,14 @@ def log_settings(self) -> None: f"> MAX BATTERY CHARGE CURRENT: {utils.MAX_BATTERY_CHARGE_CURRENT}A | " + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}A" ) + if ( + utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current + or utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current + ): + logger.info( + f"> MAX BATTERY CHARGE CURRENT: {self.max_battery_charge_current}A | " + + f"MAX BATTERY DISCHARGE CURRENT: {self.max_battery_discharge_current}A (read from BMS)" + ) logger.info(f"> CVCM: {utils.CVCM_ENABLE}") logger.info( f"> MIN CELL VOLTAGE: {utils.MIN_CELL_VOLTAGE}V | MAX CELL VOLTAGE: {utils.MAX_CELL_VOLTAGE}V" diff --git a/etc/dbus-serialbattery/bms/jkbms.py b/etc/dbus-serialbattery/bms/jkbms.py index 54da2c8a..db880704 100644 --- a/etc/dbus-serialbattery/bms/jkbms.py +++ b/etc/dbus-serialbattery/bms/jkbms.py @@ -31,8 +31,6 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count @@ -109,6 +107,18 @@ def read_status_data(self): else (current - self.CURRENT_ZERO_CONSTANT) / 100 ) + # Continued discharge current + offset = cellbyte_count + 66 + self.max_battery_discharge_current = float( + unpack_from(">H", self.get_data(status_data, b"\x97", offset, 2))[0] + ) + + # Continued charge current + offset = cellbyte_count + 72 + self.max_battery_charge_current = float( + unpack_from(">H", self.get_data(status_data, b"\x99", offset, 2))[0] + ) + offset = cellbyte_count + 18 self.soc = unpack_from(">B", self.get_data(status_data, b"\x85", offset, 1))[0] From 5c9d30a6ceebef082d30caaac56a93fea929d355 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:07:40 +0200 Subject: [PATCH 151/209] updated battery template --- etc/dbus-serialbattery/bms/battery_template.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etc/dbus-serialbattery/bms/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py index 4b308ab7..e6148e65 100644 --- a/etc/dbus-serialbattery/bms/battery_template.py +++ b/etc/dbus-serialbattery/bms/battery_template.py @@ -26,6 +26,9 @@ def test_connection(self): result = False try: result = self.read_status_data() + # get first data to show in startup log + if result: + self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False From 6a9ee0a2f3cc9b724a627f95893730326ea7394c Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:11:48 +0200 Subject: [PATCH 152/209] Progress with config limits reason --- CHANGELOG.md | 2 ++ etc/dbus-serialbattery/battery.py | 52 ++++++++++++++++++------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7019cfa..24edc9ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel * Added: Post install notes by @mr-manuel * Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel +* Added: Read charge/discharge limits from JKBMS by @mr-manuel * Added: Script to install directly from repository by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel @@ -50,6 +51,7 @@ * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/351 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/397 by @transistorgit * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/450 by @mr-manuel * Changed: Fixed black lint errors by @mr-manuel * Changed: Fixed cell balancing background for cells 17-24 by @mr-manuel * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 355e069e..bf4e5cbf 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -232,17 +232,19 @@ def manage_charge_voltage_linear(self) -> None: ) self.charge_mode = ( - "Bulk dynamic (vS: " - + str(round(voltageSum, 2)) - + " - pS: " - + str(round(penaltySum, 2)) - + ")" + "Bulk dynamic" + # + " (vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" if self.max_voltage_start_time is None - else "Absorption dynamic (vS: " - + str(round(voltageSum, 2)) - + " - pS: " - + str(round(penaltySum, 2)) - + ")" + else "Absorption dynamic" + # + "(vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" ) elif self.allow_max_voltage: @@ -263,7 +265,7 @@ def manage_charge_voltage_linear(self) -> None: and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT ): self.charge_mode += " + Balancing" - self.charge_mode += " (LM)" + self.charge_mode += " (Linear Mode)" def manage_charge_voltage_step(self) -> None: """ @@ -327,7 +329,11 @@ def manage_charge_current(self) -> None: charge_limits = [ self.max_battery_charge_current ] # gets removed after finished testing - charge_limits_new = {self.max_battery_charge_current: "None (Max Config Limit)"} + charge_limits_new = {utils.MAX_BATTERY_CHARGE_CURRENT: "Config Limit"} + + # if values are not the same, then the limit was read also from the BMS + if utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current: + charge_limits_new.update({self.max_battery_charge_current: "BMS Limit"}) if utils.CCCM_CV_ENABLE: tmp = self.calcMaxChargeCurrentReferringToCellVoltage() @@ -399,9 +405,9 @@ def manage_charge_current(self) -> None: self.charge_limitation = ( charge_limits_new[min(charge_limits_new)] - + " (" - + str(round(min(charge_limits_new), 3)) - + ")" + # + " (" + # + str(round(min(charge_limits_new), 3)) + # + ")" ) if self.control_charge_current == 0: @@ -415,9 +421,13 @@ def manage_charge_current(self) -> None: discharge_limits = [ self.max_battery_discharge_current ] # gets removed after finished testing - discharge_limits_new = { - self.max_battery_discharge_current: "None (Max Config Limit)" - } + discharge_limits_new = {utils.MAX_BATTERY_DISCHARGE_CURRENT: "Config Limit"} + + # if values are not the same, then the limit was read also from the BMS + if utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current: + discharge_limits_new.update( + {self.max_battery_discharge_current: "BMS Limit"} + ) if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() @@ -493,9 +503,9 @@ def manage_charge_current(self) -> None: self.discharge_limitation = ( discharge_limits_new[min(discharge_limits_new)] - + " (" - + str(round(min(discharge_limits_new), 3)) - + ")" + # + " (" + # + str(round(min(discharge_limits_new), 3)) + # + ")" ) if self.control_discharge_current == 0: From ebfb5bcd9c2cb7b46f8976feb5dd69fa942ee571 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:19:17 +0200 Subject: [PATCH 153/209] updated CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT default value --- etc/dbus-serialbattery/config.default.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 078d833f..716df11f 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -44,7 +44,9 @@ FLOAT_CELL_VOLTAGE = 3.35 ; Specify cell voltage diff where CVL limit is kept until diff is equal or lower CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = 0.010 ; Specify cell voltage diff where CVL limit is reset to max voltage, if value get above -CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = 0.050 +; the cells are considered as imbalanced, if the cell diff exceeds 5% of the nominal cell voltage +; e.g. 3.2 V * 5 / 100 = 0.160 V +CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = 0.080 ; -- CVL reset based on SoC option (step mode) ; Specify how long the max voltage should be kept, if reached then switch to float voltage From f44157a9aee38c961914bd08deb841dfb0faa8e2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:50:42 +0200 Subject: [PATCH 154/209] added SoC round for LLT/JBD --- etc/dbus-serialbattery/bms/lltjbd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 9bfcd7a5..8b34c920 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -158,7 +158,7 @@ def read_gen_data(self): ) = unpack_from(">HhHHHHhHHBBBBB", gen_data) self.voltage = voltage / 100 self.current = current / 100 - self.soc = 100 * capacity_remain / capacity + self.soc = round(100 * capacity_remain / capacity, 0) self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) From da3fa0781e23a3677e87b83636e23c203e6edaf8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 16:52:46 +0200 Subject: [PATCH 155/209] fixed log typo --- etc/dbus-serialbattery/battery.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index bf4e5cbf..8fd98336 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -911,8 +911,13 @@ def log_settings(self) -> None: + f"MAX BATTERY DISCHARGE CURRENT: {utils.MAX_BATTERY_DISCHARGE_CURRENT}A" ) if ( - utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current - or utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current + ( + utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current + or utils.MAX_BATTERY_DISCHARGE_CURRENT + != self.max_battery_discharge_current + ) + and self.max_battery_charge_current is not None + and self.max_battery_discharge_current is not None ): logger.info( f"> MAX BATTERY CHARGE CURRENT: {self.max_battery_charge_current}A | " From cf46dabcf80227ce2928c76f2084ea9d0ab29902 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 22:02:30 +0200 Subject: [PATCH 156/209] reworked installation procedure Bluetooth BMS is now also fetched from config.ini --- etc/dbus-serialbattery/config.default.ini | 7 ++ etc/dbus-serialbattery/installble.sh | 49 ---------- etc/dbus-serialbattery/reinstalllocal.sh | 105 ++++++++++++++++++++-- 3 files changed, 107 insertions(+), 54 deletions(-) delete mode 100755 etc/dbus-serialbattery/installble.sh diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 716df11f..3d409093 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -4,6 +4,13 @@ MAX_BATTERY_CHARGE_CURRENT = 50.0 MAX_BATTERY_DISCHARGE_CURRENT = 60.0 +; Bluetooth BMS +; Description: List the Bluetooth BMS here that you want to install +; Example with 1 BMS: Jkbms_Ble C8:47:8C:00:00:00 +; Example with 3 BMS: Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:00:00:11, Jkbms_Ble C8:47:8C:00:00:22 +#BLUETOOTH_BMS = Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:12:34:BB , Jkbms_Ble C8:47:8C:12:34:CC, Jkbms_Ble C8:47:8C:12:34:DD +BLUETOOTH_BMS = + ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) diff --git a/etc/dbus-serialbattery/installble.sh b/etc/dbus-serialbattery/installble.sh deleted file mode 100755 index 207607c4..00000000 --- a/etc/dbus-serialbattery/installble.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -# remove comment for easier troubleshooting -#set -x - -# install required packages -# TO DO: Check first if packages are already installed -opkg update -opkg install python3-misc python3-pip -pip3 install bleak - -# setup cronjob to restart Bluetooth -grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root - -# add install-script to rc.local to be ready for firmware update -filename=/data/rc.local -if [ ! -f $filename ]; then - echo "#!/bin/bash" >> $filename - chmod 755 $filename -fi -grep -qxF "sh /data/etc/dbus-serialbattery/installble.sh" $filename || echo "sh /data/etc/dbus-serialbattery/installble.sh" >> $filename - -# kill if running, needed when an adapter changes -pkill -f "python .*/dbus-serialbattery.py" - -# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 -# can be removed on second release (>1.0.0) -rm -rf /service/dbus-blebattery-* - -install_service() { - mkdir -p /service/dbus-blebattery.$1/log - echo "#!/bin/sh" > /service/dbus-blebattery.$1/log/run - echo "exec multilog t s25000 n4 /var/log/dbus-blebattery.$1" >> /service/dbus-blebattery.$1/log/run - chmod 755 /service/dbus-blebattery.$1/log/run - - echo "#!/bin/sh" > /service/dbus-blebattery.$1/run - echo "exec 2>&1" >> /service/dbus-blebattery.$1/run - echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery.$1/run - echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery.$1/run - chmod 755 /service/dbus-blebattery.$1/run -} - - -## CONFIG AREA - -## Uncomment for each adapter here, increase the number for each adapter/service - -# install_service 0 Jkbms_Ble C8:47:8C:12:34:56 -# install_service 1 Jkbms_Ble C8:47:8C:78:9A:BC diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 2b818b0a..19b804a6 100755 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -60,19 +60,114 @@ if [ ! -f $filename ]; then echo "; and add them below to persist future driver updates." >> $filename fi + + +### BLUETOOTH PART | START ### + +# get BMS list from config file +bluetooth_bms=$(awk -F "=" '/BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) +#echo $bluetooth_bms + +# clear whitespaces +bluetooth_bms_clean="$(echo $bluetooth_bms | sed 's/\s*,\s*/,/g')" +#echo $bluetooth_bms_clean + +# split into array +IFS="," read -r -a bms_array <<< "$bluetooth_bms_clean" +#declare -p bms_array +# readarray -td, bms_array <<< "$bluetooth_bms_clean,"; unset 'bms_array[-1]'; declare -p bms_array; + +length=${#bms_array[@]} +# echo $length + + +if [ $length -gt 0 ]; then + + echo "Found $length Bluetooth BMS in the config file!" + echo "" + + # install required packages + # TO DO: Check first if packages are already installed + echo "Installing required packages..." + opkg update + opkg install python3-misc python3-pip + pip3 install bleak + + # setup cronjob to restart Bluetooth + grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root + + # add install-script to rc.local to be ready for firmware update + filename=/data/rc.local + if [ ! -f $filename ]; then + echo "#!/bin/bash" >> $filename + chmod 755 $filename + fi + grep -qxF "sh /data/etc/dbus-serialbattery/installble.sh" $filename || echo "sh /data/etc/dbus-serialbattery/installble.sh" >> $filename + + # kill if running, needed when an adapter changes + pkill -f "python .*/dbus-serialbattery.py" + + # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 + # can be removed on second release (>1.0.0) + rm -rf /service/dbus-blebattery-* + + # remove existing driver to cleanup + rm -rf /service/dbus-blebattery.* + + # function to install ble battery + install_blebattery_service() { + mkdir -p /service/dbus-blebattery.$1/log + echo "#!/bin/sh" > /service/dbus-blebattery.$1/log/run + echo "exec multilog t s25000 n4 /var/log/dbus-blebattery.$1" >> /service/dbus-blebattery.$1/log/run + chmod 755 /service/dbus-blebattery.$1/log/run + + echo "#!/bin/sh" > /service/dbus-blebattery.$1/run + echo "exec 2>&1" >> /service/dbus-blebattery.$1/run + echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery.$1/run + echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery.$1/run + chmod 755 /service/dbus-blebattery.$1/run + } + + echo "Packages installed." + echo "" + + # install_blebattery_service 0 Jkbms_Ble C8:47:8C:12:34:56 + # install_blebattery_service 1 Jkbms_Ble C8:47:8C:78:9A:BC + + for (( i=0; i<${length}; i++ )); + do + echo "Installing ${bms_array[$i]} as dbus-blebattery.$i" + install_blebattery_service $i "${bms_array[$i]}" + done + +else + echo "No Bluetooth battery configuration found in \"/data/etc/dbus-serialbattery/config.ini\"." + echo "You can ignore this, if you are using only a serial connection." +fi +### BLUETOOTH PART | END ### + + # install notes echo +echo echo "SERIAL battery connection: The installation is complete. You don't have to do anything more." echo echo "BLUETOOTH battery connection: There are a few more steps to complete installation." -echo " 1. Please enable Bluetooth in the config file by adding/changing \"BLUETOOTH_ENABLED = True\"." -echo " 2. Make sure to disable Settings -> Bluetooth in the Remote-Console to prevent reconnects every minute." -echo " 3. Put your Bluetooth MAC adress in \"/data/etc/dbus-serialbattery/installble.sh\" and make sure to uncomment at least one install_service line at the bottom of the file." -echo " 4. Execute \"/data/etc/dbus-serialbattery/installble.sh\" once to create services for each Bluetooth BMS." +echo +echo " 1. Please add the Bluetooth BMS to the config file \"/data/etc/dbus-serialbattery/config.ini\" by adding \"BLUETOOTH_BMS\":" +echo " Example with 1 BMS: BLUETOOTH_BMS = Jkbms_Ble C8:47:8C:00:00:00" +echo " Example with 3 BMS: BLUETOOTH_BMS = Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:00:00:11, Jkbms_Ble C8:47:8C:00:00:22" +echo " If your Bluetooth BMS are nearby you can show the MAC address with \"bluetoothctl devices\"." +echo +echo " 2. Make sure to disable Settings -> Bluetooth in the remote console/GUI to prevent reconnects every minute." +echo +echo " 3. Re-run \"/data/etc/dbus-serialbattery/reinstalllocal.sh\", if the Bluetooth BMS were not added to the \"config.ini\" before." +echo echo " ATTENTION!" echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." echo " See https://wiki.debian.org/BluetoothUser#Using_bluetoothctl for more details." echo -echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"config.default.ini\" and add them to \"config.ini\" to persist future driver updates." +echo "CUSTOM SETTINGS: If you want to add custom settings, then check the settings you want to change in \"/data/etc/dbus-serialbattery/config.default.ini\"" +echo " and add them to \"/data/etc/dbus-serialbattery/config.ini\" to persist future driver updates." echo echo From 94b1a216b6cc07a9e10c95a5dc6d665285c1577c Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 22:44:25 +0200 Subject: [PATCH 157/209] Merge branch 'master' into jkbms_ble --- .github/ISSUE_TEMPLATE/bug_report.md | 38 - .github/ISSUE_TEMPLATE/bug_report.yml | 147 + .github/ISSUE_TEMPLATE/config.yml | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 - .github/ISSUE_TEMPLATE/feature_request.yml | 40 + .github/ISSUE_TEMPLATE/support_request.yml | 125 + .github/workflows/github-pages.yml | 72 + .github/workflows/release.yml | 7 +- docs/.gitignore | 20 + docs/README.md | 31 + docs/babel.config.js | 3 + docs/docs/faq/index.md | 241 + docs/docs/general/features.md | 153 + docs/docs/general/index.md | 52 + docs/docs/general/install.md | 246 + docs/docs/general/supported-bms.md | 68 + docs/docs/troubleshoot/faq.md | 13 + docs/docs/troubleshoot/index.md | 176 + docs/docusaurus.config.js | 141 + docs/package.json | 46 + docs/screenshots/bms-daly.jpg | Bin 0 -> 118733 bytes docs/screenshots/bms-xiaoxian-android.jpg | Bin 0 -> 74310 bytes docs/screenshots/bms-xiaoxian-ios.jpg | Bin 0 -> 98670 bytes docs/screenshots/faq-lfp-curves.jpg | Bin 0 -> 218437 bytes docs/screenshots/settings-dvcc.png | Bin 0 -> 37610 bytes docs/screenshots/venus-os_001.png | Bin 0 -> 46750 bytes docs/screenshots/venus-os_002.png | Bin 0 -> 39017 bytes docs/screenshots/venus-os_003.png | Bin 0 -> 38508 bytes docs/screenshots/venus-os_004.png | Bin 0 -> 29131 bytes docs/screenshots/venus-os_005.png | Bin 0 -> 33257 bytes docs/screenshots/venus-os_006.png | Bin 0 -> 39955 bytes docs/screenshots/venus-os_007.png | Bin 0 -> 51240 bytes docs/screenshots/venus-os_008.png | Bin 0 -> 34903 bytes docs/screenshots/venus-os_009.png | Bin 0 -> 23828 bytes docs/screenshots/venus-os_010.png | Bin 0 -> 26842 bytes docs/screenshots/venus-os_011.png | Bin 0 -> 30431 bytes docs/screenshots/venus-os_012.png | Bin 0 -> 30695 bytes docs/screenshots/venus-os_013.png | Bin 0 -> 32555 bytes docs/screenshots/vrm-charge-limits.png | Bin 0 -> 40317 bytes docs/screenshots/vrm-portal_001.png | Bin 0 -> 24737 bytes docs/screenshots/vrm-portal_002.png | Bin 0 -> 21325 bytes docs/screenshots/vrm-portal_003.png | Bin 0 -> 24383 bytes docs/screenshots/vrm-portal_004.png | Bin 0 -> 48559 bytes docs/screenshots/vrm-portal_005.png | Bin 0 -> 60980 bytes docs/screenshots/vrm-portal_006.png | Bin 0 -> 44756 bytes docs/screenshots/vrm-portal_007.png | Bin 0 -> 47252 bytes docs/screenshots/vrm-portal_008.png | Bin 0 -> 55744 bytes docs/screenshots/vrm-portal_009.png | Bin 0 -> 48820 bytes docs/sidebars.js | 38 + docs/src/css/custom.css | 55 + docs/static/.nojekyll | 0 docs/static/img/favicon.ico | Bin 0 -> 3626 bytes docs/static/img/logo.svg | 1 + docs/static/matomo.js | 12 + docs/tsconfig.json | 7 + docs/yarn.lock | 7615 ++++++++++++++++++++ 56 files changed, 9315 insertions(+), 62 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/support_request.yml create mode 100644 .github/workflows/github-pages.yml create mode 100644 docs/.gitignore create mode 100644 docs/README.md create mode 100644 docs/babel.config.js create mode 100644 docs/docs/faq/index.md create mode 100644 docs/docs/general/features.md create mode 100644 docs/docs/general/index.md create mode 100644 docs/docs/general/install.md create mode 100644 docs/docs/general/supported-bms.md create mode 100644 docs/docs/troubleshoot/faq.md create mode 100644 docs/docs/troubleshoot/index.md create mode 100644 docs/docusaurus.config.js create mode 100644 docs/package.json create mode 100644 docs/screenshots/bms-daly.jpg create mode 100644 docs/screenshots/bms-xiaoxian-android.jpg create mode 100644 docs/screenshots/bms-xiaoxian-ios.jpg create mode 100644 docs/screenshots/faq-lfp-curves.jpg create mode 100644 docs/screenshots/settings-dvcc.png create mode 100644 docs/screenshots/venus-os_001.png create mode 100644 docs/screenshots/venus-os_002.png create mode 100644 docs/screenshots/venus-os_003.png create mode 100644 docs/screenshots/venus-os_004.png create mode 100644 docs/screenshots/venus-os_005.png create mode 100644 docs/screenshots/venus-os_006.png create mode 100644 docs/screenshots/venus-os_007.png create mode 100644 docs/screenshots/venus-os_008.png create mode 100644 docs/screenshots/venus-os_009.png create mode 100644 docs/screenshots/venus-os_010.png create mode 100644 docs/screenshots/venus-os_011.png create mode 100644 docs/screenshots/venus-os_012.png create mode 100644 docs/screenshots/venus-os_013.png create mode 100644 docs/screenshots/vrm-charge-limits.png create mode 100644 docs/screenshots/vrm-portal_001.png create mode 100644 docs/screenshots/vrm-portal_002.png create mode 100644 docs/screenshots/vrm-portal_003.png create mode 100644 docs/screenshots/vrm-portal_004.png create mode 100644 docs/screenshots/vrm-portal_005.png create mode 100644 docs/screenshots/vrm-portal_006.png create mode 100644 docs/screenshots/vrm-portal_007.png create mode 100644 docs/screenshots/vrm-portal_008.png create mode 100644 docs/screenshots/vrm-portal_009.png create mode 100644 docs/sidebars.js create mode 100644 docs/src/css/custom.css create mode 100644 docs/static/.nojekyll create mode 100644 docs/static/img/favicon.ico create mode 100644 docs/static/img/logo.svg create mode 100644 docs/static/matomo.js create mode 100644 docs/tsconfig.json create mode 100644 docs/yarn.lock diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 224f77d4..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**VenusOS (please complete the following information):** - - Device type: [e.g. VenusGX, Cerbo, Raspberry Pi] - - Firmware Version [e.g. 262] - -**Battery/BMS (please complete the following information):** - - BMS/Battery type: [e.g. LTT, Daly] - - Cells: [e.g. 16] - - Interface: [e.g. USB-RS485, USB-RS232, Ve.Direct] - -**Additional context** -Add any other context about the problem here. - -Please add multiline code or logfiles by useing three backticks (```) or three tildes (~~~) on the lines before and after the code block. See https://www.markdownguide.org/extended-syntax/#fenced-code-blocks for more details. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..df191642 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,147 @@ +name: Bug report +description: Report a bug that you found, e.g. something is not working as expected +# title: "" +labels: ["bug"] +assignees: [] +body: + - type: markdown + attributes: + value: | + ## Thanks for helping us to improve the driver! + + In the case you have difficulties to write in english, try the [deepl.com](https://www.deepl.com) translator. + + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Paste also screenshots, if needed. + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: How to reproduce + description: Please describe in clear steps how to reproduce the issue. Paste also screenshots, if needed. + value: | + Steps to reproduce the behavior: + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. Paste also screenshots, if needed. + validations: + required: true + + - type: input + id: driver_version + attributes: + label: Driver version + description: Visible in the [driver log files](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/#driver-log-files) or in the remote console/GUI under `SerialBattery` -> `Device` -> `Firmware version` + validations: + required: true + + - type: dropdown + id: venus_os_device_type + attributes: + label: Venus OS device type + description: Select your Venus OS device type. + multiple: false + # https://github.com/victronenergy/venus/wiki/machines + options: + - Please select + - CCGX + - Cerbo GX + - Ekrano GX + - GX Card (integrated in Victron device) + - Octo GX + - Raspberry Pi + - VANcu GX + - Venus_GX + validations: + required: true + + - type: input + id: venus_os_version + attributes: + label: Venus OS version + description: Visible in the remote console/GUI under `Settings` -> `Firmware` -> `Firmware version` + placeholder: e.g. v2.94 + validations: + required: true + + - type: dropdown + id: bms_type + attributes: + label: BMS type + description: Select your used BMS type. + multiple: true + # https://louisvdw.github.io/dbus-serialbattery/general/supported-bms + options: + - ANT BMS + - Daly Smart BMS + - ECS GreenMeter + - HLPdataBMS4S + - JKBMS / Heltec BMS + - Life/Tian Power + - MNB spi BMS + - Renogy BMS + - Revov + - Seplos + - Sinowealth + - Smart BMS (LLT, JBD, Overkill Solar) + validations: + required: true + + - type: input + id: cell_count + attributes: + label: Cell count + description: Specify how many cells your battery has. + validations: + required: true + + - type: dropdown + id: connection_type + attributes: + label: Connection type + description: Select how you connected your battery to the Venus OS device. + multiple: false + options: + - Please select + - Serial USB adapter to TTL + - Serial USB adapter to RS485 + - Bluetooth + validations: + required: true + + - type: textarea + id: config + attributes: + label: Config file + description: Paste your config file here. See [here](https://louisvdw.github.io/dbus-serialbattery/general/install#settings-locationpath) where you can find it. to get This will be automatically formatted into code, so no need for backticks. + render: ini + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output here. See [here](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/#driver-log-files) how to get the driver log files. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: true + + - type: textarea + id: other + attributes: + label: Any other information that may be helpful diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..400810a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +blank_issues_enabled: false + +contact_links: + - name: Troubleshoot + about: Take a look how to troubleshoot. + url: https://louisvdw.github.io/dbus-serialbattery/troubleshoot/ + + - name: FAQ (Frequently Asked Questions) + about: Take a look on the FAQ's. + url: https://louisvdw.github.io/dbus-serialbattery/faq/ diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7d..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..506d6e89 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,40 @@ +name: Feature request +description: Suggest an idea for this project +# title: "" +labels: ["enhancement"] +assignees: [] +body: + - type: markdown + attributes: + value: | + ## Thanks for helping us to improve the driver! + + In the case you have difficulties to write in english, try the [deepl.com](https://www.deepl.com) translator. + + - type: textarea + id: idea + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. + placeholder: Ex. I'm always frustrated when [...] + + - type: textarea + id: solution + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + placeholder: + + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + placeholder: + + - type: textarea + id: other + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + placeholder: diff --git a/.github/ISSUE_TEMPLATE/support_request.yml b/.github/ISSUE_TEMPLATE/support_request.yml new file mode 100644 index 00000000..ae469d62 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_request.yml @@ -0,0 +1,125 @@ +name: Support request +description: Report a bug that you found, e.g. something is not working as expected +# title: "" +labels: ["support"] +assignees: [] +body: + - type: markdown + attributes: + value: | + ## Before opening a support request, please check first [How to troubleshoot](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/) and the [FAQ (Frequently Asked Questions)](https://louisvdw.github.io/dbus-serialbattery/faq/) page + + In the case you have difficulties to write in english, try the [deepl.com](https://www.deepl.com) translator. + + - type: textarea + id: description + attributes: + label: Describe the problem + description: A clear and concise description of what the problem is. Paste also screenshots and pictures, if needed. The more informations you give, the faster we find a solution. + validations: + required: true + + - type: input + id: driver_version + attributes: + label: Driver version + description: Visible in the [driver log files](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/#driver-log-files) or in the remote console/GUI under `SerialBattery` -> `Device` -> `Firmware version` + validations: + required: true + + - type: dropdown + id: venus_os_device_type + attributes: + label: Venus OS device type + description: Select your Venus OS device type. + multiple: false + # https://github.com/victronenergy/venus/wiki/machines + options: + - Please select + - CCGX + - Cerbo GX + - Ekrano GX + - GX Card (integrated in Victron device) + - Octo GX + - Raspberry Pi + - VANcu GX + - Venus_GX + validations: + required: true + + - type: input + id: venus_os_version + attributes: + label: Venus OS version + description: Visible in the remote console/GUI under `Settings` -> `Firmware` -> `Firmware version` + placeholder: e.g. v2.94 + validations: + required: true + + - type: dropdown + id: bms_type + attributes: + label: BMS type + description: Select your used BMS type. + multiple: true + # https://louisvdw.github.io/dbus-serialbattery/general/supported-bms + options: + - ANT BMS + - Daly Smart BMS + - ECS GreenMeter + - HLPdataBMS4S + - JKBMS / Heltec BMS + - Life/Tian Power + - MNB spi BMS + - Renogy BMS + - Revov + - Seplos + - Sinowealth + - Smart BMS (LLT, JBD, Overkill Solar) + validations: + required: true + + - type: input + id: cell_count + attributes: + label: Cell count + description: Specify how many cells your battery has. + validations: + required: true + + - type: dropdown + id: connection_type + attributes: + label: Connection type + description: Select how you connected your battery to the Venus OS device. + multiple: false + options: + - Please select + - Serial USB adapter to TTL + - Serial USB adapter to RS485 + - Bluetooth + validations: + required: true + + - type: textarea + id: config + attributes: + label: Config file + description: Paste your config file here. See [here](https://louisvdw.github.io/dbus-serialbattery/general/install#settings-locationpath) where you can find it. to get This will be automatically formatted into code, so no need for backticks. + render: ini + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output here. See [here](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/#driver-log-files) how to get the driver log files. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: true + + - type: textarea + id: other + attributes: + label: Any other information that may be helpful diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml new file mode 100644 index 00000000..0b30880c --- /dev/null +++ b/.github/workflows/github-pages.yml @@ -0,0 +1,72 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + # Run on changes in the master branch + #branches: [master]: + + # Run on changes in the docs folder + paths: + - docs/** + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +env: + # Hosted GitHub runners have 7 GB of memory available, let's use 6 GB + NODE_OPTIONS: --max-old-space-size=6144 + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + + #defaults: + # run: + # working-directory: 'docs' # Here the path to the folder where package-lock.json is located. + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: yarn + cache-dependency-path: './docs/yarn.lock' # THIS PATTERN did the trick for me. + + - name: Install dependencies + run: yarn install --cwd ./docs --frozen-lockfile --non-interactive + + - name: Build + run: yarn --cwd ./docs build + + - name: Setup Pages + uses: actions/configure-pages@v1 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload entire repository + path: docs/build + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9ad92fb..c3fe7829 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,12 +19,11 @@ jobs: find . -type f -name "run" -exec chmod +x {} \; tar -czvf venus-data.tar.gz \ --mode='a+rwX' \ - --exclude battery_template.py \ --exclude __pycache__ \ + --exclude bms/battery_template.py \ + --exclude bms/revov.py \ + --exclude bms/test_max17853.py \ --exclude restartservice.sh \ - --exclude revov.py \ - --exclude test_max17853.py \ - --exclude util_max17853.py \ etc/dbus-serialbattery/ - name: Release diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..b2d6de30 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..989b0597 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,31 @@ +# Website + +This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. + +## Contribution + +To contribute to the documentation you have to fork this repository, edit/add the content in `docs` and then open a pull request to merge your changes. + +## Local Development + +```bash +$ yarn +``` + +This command installs all missing dependencies. + +### Build + +```bash +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +## Start local server + +```bash +npm run serve +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. diff --git a/docs/babel.config.js b/docs/babel.config.js new file mode 100644 index 00000000..e00595da --- /dev/null +++ b/docs/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/docs/docs/faq/index.md b/docs/docs/faq/index.md new file mode 100644 index 00000000..f9bd7846 --- /dev/null +++ b/docs/docs/faq/index.md @@ -0,0 +1,241 @@ +--- +id: faq +title: FAQ (Frequently Asked Questions) +sidebar_position: 3 +--- + +# FAQ (Frequently Asked Questions) + +## How to change the default limits +See [this page](../general/install#how-to-change-the-default-limits). + + +## How to edit `utils.py` or `config.ini` +See [this page](../general/install#how-to-change-the-default-limits). + + +## How to enable a disabled BMS +See [this page](../general/install#how-to-change-the-default-limits). + + +## What is the username and password of the SSH connection? + +See the Victron Energy documentation how to get [root access](https://www.victronenergy.com/live/ccgx:root_access#root_access). + +## Why do I need a BMS for lithium batteries? +Lithuim cells are great at storing energy, but they can be dangerous. An overcharged cell can cause a fire. A Battery Management System (BMS) first priority is to protect the cells in your battery from any potential hazards. + +The BMS will prevent your battery reaching an unsafe situation (it will disconnect the charge or discharge) and help with the state of each cell inside the battery so that your battery will last longer. + + +## Which BMS should I buy? +Most of the BMS that the driver support will work fine and the driver does support most features for all the BMS brands. See the [comparison table](../general/features#bms-feature-comparison) for any small differenaces. + +Find the BMS that fits your budget with the features that you need. + +The balancers on Smart Daly BMS don't seem to work that well so many users have opted to add an external balancer to help with that. + +Also the way that Daly implemented their communication protocol gives for a much slower data retrial which means slower response to events by the driver. + +If you own a Daly, then it will work just fine, but if you still need to buy your BMS then one of the other supported BMS would be a better choice. + + +## Which serial adapter/cable should I use? +Most USB serial adapters and cables should work fine. You need to use the adapter for the UART type that your BMS use, which is normally TTL, RS232, RS485 or even SPI. + +Those adapters based on the **FDTI** or **CH340** chips are the easiest to use because the GX opperating system already include drivers for them. + +Cable preferences: + +1. Best option is the UART cable/box for your BMS. These use connectors matching your BMS and minimise potential problems with errors from loose connections. +2. Isolated ([galvanic isolation](https://en.wikipedia.org/wiki/Galvanic_isolation)) cables. These are more expensive, but have electrical protection built in making them safer. +3. Any adapter that work with your BMS UART. + +> 🚨 **NB! Only connect Rx & Tx or A & B to the BMS,** if you are NOT using an isolated ([galvanic isolation](https://en.wikipedia.org/wiki/Galvanic_isolation)) cable or adapter. This prevents the current to flow through the adapter, if the BMS cuts the ground. Else it will destroy your BMS, GX device or Raspberry Pi. + +## Which UART connection is the best to use (TTL/RS232/RS485)? +The driver works the same with all the supported UART types. Most BMS will use the `3.3V` TTL (which some would lable as UART) and/or RS485 (`5V`). Victron's VE.Direct is RS232 (`12V`), but not many BMS use that. + +You need to match the UART type with what your BMS support. + +If the Bluetooth module for your BMS use the UART, then you will need to remove that to plug in your USB adapter, if you do not have another UART available. After your initial setup you do not need the Bluetooth, but you will not be able to connect to it with the phone app. + + +## Do I need a SmartShunt as well? +No you don't. All BMS do measure the total battery voltage and most do use a shunt to do that already. + +The Smartshunt is very well build and most likely better calibrated, but it can only measure the total battery voltage and the current input/output. There are no sensors for the cells inside the battery so the Smartshunt have much less information to work with. + +It is best to let the BMS be the Battery Monitor. + +You can add both to your system and in the newer Venus OS firmwares you can select to upload both values to the VRM cloud. + + +## Can the driver be used as monitor only? + +> Please also read [Should I set the Smartshunt or the BMS as the Battery Monitor?](../faq/#should-i-set-the-smartshunt-or-the-bms-as-the-battery-monitor) + +Yes it can, but there are certain limitations. + +### Venus OS `< v3.00` +Select another or `No battery monitor` in the remote console under `Settings -> System setup -> Battery monitor` and disable `DVCC` in the remote console under `Settings -> DVCC`. + +The `DVCC` has to be disabled since Venus OS selects the first BMS available in Venus OS `< v3.00`. Pay attention, that in this case also other BMS cannot control the charger (CVL, CCL, DCL). Please consider to upgrade to the latest Venus OS version in this case. + +### Venus OS `>= v3.00` +Select another or `No battery monitor` in the remote console under `Settings -> System setup -> Battery monitor` and another or `No BMS control` in the remote console under `Settings -> DVCC -> Controlling BMS`. + +## Should I set the Smartshunt or the BMS as the Battery Monitor? +Set the BMS as the Battery Monitor of your system so that it can read the alarm and cell data to manage your GX system. If you don't, then these alarms will be ignored and the system will keep on (dis)charging the battery even if a problem alarm is raised. The BMS will react by disconnecting the battery for protection and your inverter will go offline. + + +## Why is the max cell voltage set to `3.45V`? +Most home power systems use LiFePo4 cells that are `3.2V`. This explanation is the same but with different voltages for a `3.7V` cell system. + +The default `MAX_CELL_VOLTAGE` and `MIN_CELL_VOLTAGE` that is set in the driver is to get your battery to full charge without any cell going into protection. This is used to calculate the max voltage for the battery that we ask the chargers to charge the battery too. + +`3.65V` will be the protection voltage of a cell for most BMS while `3.45V` is considered full charge. + +If you set your charger to aim for `3.65V` it means you are aiming to reach the protection voltage (which it will, and raise an alert). Even `3.64V` is too close. + +If you look at your cells you will see that it takes hours of charging to go from `3.2V` to `3.4V`, but from `3.45V` to `3.6+V` can be in just a second. This give no time for balancing to kick in, thus one cell will reach `3.65V` while most of the rest will still be around `3.45V` (assuming all your cells are closely balanced. This is much worse if they are not). This all gets multiplied by the cell count that you have. + +An example might help (using a 8 cell battery): + +* If we aim for `3.45V` per cell that will tell the charger to charge to `27.60V` (`3.45V x 8`) +* If we aim for `3.64V` per cell that will tell the charger to charge to `29.12V` (`3.64V x 8`) + +Now say all our cells are almost full. That would mean they are close to `3.45V` or `27.6V` while we are requesting the battery to go up to `29.12V` or another `1.52V`. + +The first cell that is a bit more full than the rest will jump from `3.45V` to `3.65V` and raise an alarm, but it is only an increase of `0.2V` which mean while one cell should be protected we are asking the charger to increase the voltage another `1.32V` cause it thinks we are not full yet. + +If this continues the BMS will disconnect and your power will trip. + +Now, if you still do want to aim for `3.65V` per cell you will have to change the setting on your BMS so that the BMS protection only kick in at a higher voltage (e.g. `3.85V`), but I WOULD NOT RECOMMEND THIS, if you want your battery to last a long time. + +You get most of the power from the cells between `3.1V - 3.45V` and you will have less issues. + +![lfp-charge-discharge-curves](../../screenshots/faq-lfp-curves.jpg) + + +## Why is the charging/discharging current limit (CCL/DCL) smaller than the set one? + +### Driver version `<= v0.14.3` +Check in the `utils.py`, if you have set one of this to true. If yes, then you also have to change the corresponding limits. + +* `CCCM_CV_ENABLE = True` then modify the highest value (60) `MAX_CHARGE_CURRENT_CV = [0, 2, 30, 60]` to the same value or bigger as `MAX_BATTERY_CHARGE_CURRENT` +* `DCCM_CV_ENABLE = True` then modify `MAX_DISCHARGE_CURRENT_CV = [0, 5, 30, 60]` +* `CCCM_T_ENABLE = True` then modify `MAX_CHARGE_CURRENT_T = [0, 28, 60, 60, 28, 0]` +* `DCCM_T_ENABLE = True` then modify `MAX_DISCHARGE_CURRENT_T = [0, 28, 60, 60, 28, 0]` + +### Driver version `>= v1.0.0` +The limits are based on percentages of `MAX_CHARGE_CURRENT_CV` and `MAX_DISCHARGE_CURRENT_CV` values, so there is no need for additional modifications. Additionaly you see in the remote console/GUI under `SerialBattery` → `Parameters` why it's limited. + +![VenusOS](../../screenshots/venus-os_013.png) + + +## Does the driver work for `3.7V` based cells also? +Yes, but you will [need to adjust](../general/install#how-to-change-the-default-limits) the `MAX_CELL_VOLTAGE` and `MIN_CELL_VOLTAGE` values for `3.7V` cells instead of the default `3.2V` cells. + +Recommended values for `3.7V` cells are: + +```ini +MAX_CELL_VOLTAGE = 4.0 +MIN_CELL_VOLTAGE = 3.6 +``` + + +## Why do I get a Low Voltage alarm? + +> Not elaborated completely, in the meanwhile see infos below + +* [Low Battery Voltage Alarm if /Info/MaxDischargeCurrent = 0](https://github.com/Louisvdw/dbus-serialbattery/issues/407) +* [Undervoltage alarm - why?](https://github.com/Louisvdw/dbus-serialbattery/issues/363) + + +## Why is DCL jumping from/to `0`? + +> Not elaborated completely, in the meanwhile see infos below + +* [DCL is "jumping" - should be 0 ?! - JKBMS](https://github.com/Louisvdw/dbus-serialbattery/issues/371) + +What is happening is that as soon as no more current is drawn the cell values starts to recover and the BMS release (the charger should only kick in when you are below the min SoC value you have set in the ESS settings). + +What helped me here was reducing the Cut-off voltages in the ESS asisstant (there are 4 voltages for different currents) - when the battery voltage dropped to this cut-off voltage, the discharging will stop and the "sustain voltage" will jump in until the battery voltage rises up over the "above-cut-off" value. + +Important!!! - When the ESS asisstant is activated, all the 3 "DC input low voltage" settings under the "Inverter" tab are completely ignored by the MP2. + +**How to solve this:** +1. Current setting `> 0` (but I did not test this) +2. Reducing the ESS "Cut-off voltage" like I mentioned above + + +## Why do I get a High Voltage alarm? +If you receive High Voltage alarms that would indicate your battery is: + +1. Not set up correctly and you are using the wrong charge voltages +2. It has cell imbalance issues + +So asuming you have set the max battery voltage for what the battery require, you then need to help the battery to get the cells balanced. You do that by lowering the max voltage to a level where you don’t get high voltage alarms anymore and then slowly over a few weeks you can increase the max voltage to where it should be. This will give the balancers time to work. + +In your GX settings go to the DVCC menu and activate the "Limit managed battery charge voltage" feature and lower the "Maximum Charge Voltage". +Drop your voltage to `0.2V` lees that normal and then increase it every day by `0.05V` if you did not get a High Voltage alarm during the previous day. If you did get an alarm leave it unchanged for another day. + +Do this until you get to the original max charge voltage for your battery. + +This will be much faster to do if you use the Keep Batteries changed in ESS option while you are doing this. + +Balancing works when ever 1 cell go above the balance threshold, so you are trying to find the battery voltage where that one cell is above the threshold but below the high voltage alarm (e.g. `3.45V - 3.65V`) and then giving the balancers time to work down the high cell with the small balance currents (`50mA` to `200mA`). + + +## Why is the battery current inverted? +Some Daly BMS send the current as inverted value. This can be corrected by setting `INVERT_CURRENT_MEASUREMENT` to `-1` in the `utils.py` or `config.ini`. See [How to edit `utils.py` or `config.ini`](../general/install#how-to-edit-utilspy-or-configini). + + +## What can I do, if the BMS communication is unstable? + +Most unstable communications arise due to: + +* Cabeling: Check your cables again and make sure that all solder points are making good connection. +* Missing shielded cable: If the connection gets unstable on greater battery currents and your serial cable is near or along the battery cable, then try to use a shielded cable. +* Damaged/Defective serial adapters: Try another serial adapter. +* Cheap USB Hubs: Make sure you are using a qualitative USB Hub with enough power. +* Raspberry Pi: Do not use a charger for powering the Raspberry Pi. Instead buy a power supply with enough power. + + +## Fix white screen after install +Normally this will happen, if you were on an older firmware for your GX. + +You can remove the GUI changes or update your GX firmware to solve this. + + +### Remove GUI changes +```bash +cp -f /opt/victronenergy/gui/qml/PageBattery.qml.backup /opt/victronenergy/gui/qml/PageBattery.qml +reboot +``` + + +### Update to the latest firmware +```bash +/opt/victronenergy/swupdate-scripts/check-updates.sh -update -force +``` + + +## How many USB to serial adapters can I connect? +It seems that the Victron GX device has a limit of maximum 8 USB to serial adapters. See [Serial battery driver not found if other VE.direct-USB devices are present](https://github.com/Louisvdw/dbus-serialbattery/issues/422) + + + +## `$'\r': command not found` or `syntax error: unexpected end of file` + +This indicates, that the line endlings were changed during the upload from `LF` to `CRLF`. Run this commands to repair the files: + +```bash +sed -i 's/\r//' /data/etc/dbus-serialbattery/*.sh +sed -i 's/\r//' /data/etc/dbus-serialbattery/*.py +sed -i 's/\r//' /data/etc/dbus-serialbattery/service/run +sed -i 's/\r//' /data/etc/dbus-serialbattery/service/log/run +``` + +Now reboot the device. If this doesn't help, then download/unpack and reinstall the driver again. diff --git a/docs/docs/general/features.md b/docs/docs/general/features.md new file mode 100644 index 00000000..a7b2434c --- /dev/null +++ b/docs/docs/general/features.md @@ -0,0 +1,153 @@ +--- +id: features +title: Features +sidebar_position: 2 +--- + +# Features + +The driver can handle batteries from 3 to 32 cells. It will act as Battery Monitor inside Venus OS and update the following values: + +* Voltage +* Current +* Power +* SoC (State of Charge) +* Battery temperature +* Mosfet temperature +* Consumed Ah +* Time-to-go + +* Min/max cell voltages +* Min/max temperature (depending on BMS) +* Installed capacity +* Available capacity + +* Cell details (depending on BMS) + * Min + * Max + * Diff + * Cell voltage 1 - 32 + +* Raise alarms from the BMS + +* History of charge cycles + +* Charge current control management (CCCM) + +* Set battery parameters (DVCC) + * Charge Voltage Limit (CVL) + * Charge Current Limit (CCL) + * Discharge Current Limit (DCL) + * CVL (battery max) automatically adjusted by `cell count` \* `3.45V` + * Battery min automatically adjusted by `cell count` \* `3.1V` + +### Screenshots + +![VenusOS](../../screenshots/venus-os_003.png) +![VenusOS](../../screenshots/venus-os_005.png) +![VenusOS](../../screenshots/venus-os_006.png) +![VenusOS](../../screenshots/venus-os_007.png) +![VenusOS](../../screenshots/venus-os_008.png) +![VenusOS](../../screenshots/venus-os_009.png) +![VenusOS](../../screenshots/venus-os_010.png) +![VenusOS](../../screenshots/venus-os_013.png) + + +## Charge current control management +CCCM limits the current when the battery is close to full or close to empty. +When your battery is full, the reduced charge current will give the balancers in your BMS time to work. +When your battery is close to empty the reduced dicharge current will limit that a sudden large load will pull your battery cells below their protection values. + +### Limitation modes +The limits can be applied in Step or Linear mode. +* **Step** use hard boundaries that will apply recognisable step values and use less processing power (DEFAULT) +* **Linear** will give a gradual change from one limit range to the next + +### CCCM attributes +You can set CCCM limits for 3 attributes which can be enabled / disabled and adjusted by settings in `utils.py` (driver version `<= v0.14.3`) or `config.ini` (driver version `>= v1.0.0`). +The smallest limit from all enabled will apply. + +### Cell voltage +* `CCCM_CV_ENABLE = True/False` +* `DCCM_CV_ENABLE = True/False` + +CCCM limits the charge/discharge current depending on the highest/lowest cell voltages + +* between `3.50V - 3.55V` → `2A` charge +* between `3.45V - 3.50V` → `30A` charge +* between `3.30V - 3.45V` → `60A` + +* `3.30V - 3.10V` → max charge and max discharge (`60A`) + +* between `2.90V - 3.10V` → `30A` discharge +* between `2.8V - 2.9V` → `5A `discharge +* below `<= 2.70V` → `0A` discharge + +### Temprature + +* `CCCM_T_ENABLE = True/False` +* `DCCM_T_ENABLE = True/False` + +CCCM limits the charge/discharge current depending on the highest/lowest temperature sensor values +* Charging will be `0A` if below `0°C` or above `55°C` +* Discharging will be `0A` if below `-20°C` or above `55°C` + +### SoC (State of Charge) from the BMS +* `CCCM_SOC_ENABLE = True/False` +* `DCCM_SOC_ENABLE = True/False` + +CCCM limits the charge/discharge current depending on the SoC + +* between `99% - 100%` → `5A` charge +* between `95% - 98%` → 1/4 max charge +* between `91% - 95%` → 1/2 max charge + +* `30% - 91%` → max charge and max discharge + +* between `20% - 30%` → 1/2 max discharge +* between `10% - 20%` → 1/4 max discharge +* below `<= 10%` → `5A` + +![VenusOS values](../../screenshots/vrm-charge-limits.png) + +## Charge voltage control management + +### Cell voltage penalty +If the cell voltage reaches a specific value, then subtract a penalty from the CVL. +Detailed info can be found here: [CCL/DCL depending on cell-voltage does not turn off charging completely, still overvoltage alarm](https://github.com/Louisvdw/dbus-serialbattery/issues/297#issuecomment-1327142635) + +### Float voltage emulation +If the `MAX_CELL_VOLTAGE` \* `cell count` is reached for `MAX_VOLTAGE_TIME_SEC` then the CVL changes to `FLOAT_CELL_VOLTAGE` \* `cell count`. Max voltage could be reached again if the SoC gets under `SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT`. + +## BMS feature comparison + +| Feature | Ant | Daly | ECS | HLPdataBMS4S | JK BMS | Life/Tian Power | LLT/JBD | MNB (1) | Renogy | Seplos | Sinowealth (1) | +| ---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| Voltage | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Current | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Power | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| State Of Charge | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Battery temperature | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| MOSFET temperature | No | No | No | No | Yes | No | Yes | No | No | No | No | +| Consumed Ah | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Time-to-go | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | +| Min/max cell voltages | Yes | Yes | No | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | +| Min/max temperature | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Installed capacity | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Available capacity | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Cell details | No | Yes | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | ? | +| Balancing status | Yes | No | Yes | No | Yes | Yes | No | No | No | No | ? | +| Raise alarms from the BMS | Yes | Yes | Yes (2) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | +| History of charge cycles | Yes | Yes | No | No | Yes | Yes | Yes | No | Yes | Yes | Yes | +| Get CCL/DCL from the BMS | No | No | No | No | Yes | No | No | No | No | No | No | +| Charge current control management (CCCM) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Set battery parameters (DVCC) | Calc | Calc | Yes | Yes | Calc | Calc | Calc | Yes | Calc | Calc | Calc | + + +`Calc` means that the value is calculated by the driver. + +`?` means that it's unknown, if the value is fetched. It has to be verified. If you know this, please update this page. + +(1) Disabled by default. They can be enabled by uncommenting in `dbus-serialbattery.py`. + +(2) No cells yet. diff --git a/docs/docs/general/index.md b/docs/docs/general/index.md new file mode 100644 index 00000000..6e81ff45 --- /dev/null +++ b/docs/docs/general/index.md @@ -0,0 +1,52 @@ +--- +id: index +title: Introduction +slug: / +sidebar_position: 1 +--- + +# dbus-serialbattery + +This is a driver for Venus OS devices (any GX device sold by Victron or a Raspberry Pi running the Venus OS image). + +The driver will communicate with a Battery Management System (BMS) that support serial communication (RS232, RS485 or TTL UART) and publish this data to the Venus OS system. The main purpose is to act as a Battery Monitor in your GX and supply State of Charge (SoC) and other values to the inverter. + +## Supporting this project + +If you find this driver helpful please consider supporting this project. You can buy me a Ko-Fi or get in contact, if you would like to donate hardware. + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z73LCW1) or using [Paypal.me](https://paypal.me/innernet) + +## Requirements + +* GX device or Raspberry Pi running Venus OS version `v2.80` or later. + +## Screenshots + +### Venus OS + +![VenusOS](../../screenshots/venus-os_001.png) +![VenusOS](../../screenshots/venus-os_002.png) +![VenusOS](../../screenshots/venus-os_003.png) +![VenusOS](../../screenshots/venus-os_004.png) +![VenusOS](../../screenshots/venus-os_005.png) +![VenusOS](../../screenshots/venus-os_006.png) +![VenusOS](../../screenshots/venus-os_007.png) +![VenusOS](../../screenshots/venus-os_008.png) +![VenusOS](../../screenshots/venus-os_009.png) +![VenusOS](../../screenshots/venus-os_010.png) +![VenusOS](../../screenshots/venus-os_011.png) +![VenusOS](../../screenshots/venus-os_012.png) +![VenusOS](../../screenshots/venus-os_013.png) + +### VRM Portal + +![VenusOS](../../screenshots/vrm-portal_001.png) +![VenusOS](../../screenshots/vrm-portal_002.png) +![VenusOS](../../screenshots/vrm-portal_003.png) +![VenusOS](../../screenshots/vrm-portal_004.png) +![VenusOS](../../screenshots/vrm-portal_005.png) +![VenusOS](../../screenshots/vrm-portal_006.png) +![VenusOS](../../screenshots/vrm-portal_007.png) +![VenusOS](../../screenshots/vrm-portal_008.png) +![VenusOS](../../screenshots/vrm-portal_009.png) diff --git a/docs/docs/general/install.md b/docs/docs/general/install.md new file mode 100644 index 00000000..28e13577 --- /dev/null +++ b/docs/docs/general/install.md @@ -0,0 +1,246 @@ +--- +id: install +title: How to install, update, disable, enable and uninstall +sidebar_position: 4 +# Display h2 to h4 headings +toc_min_heading_level: 2 +toc_max_heading_level: 4 +--- + +# How to install, update, disable, enable and uninstall + +## 🚨 NB! Before you begin + +> The driver does not do any setup of your BMS/battery. You need to have a working battery before you start. + +> From the driver release `v0.12` you need to be running Venus OS `v2.80` or higher. + +> Note! The current version require Venus OS `v2.91` or `v2.92`. Else you will see a white screen after the install. I will remove this note when that issue is fixed. + +The driver currently implement some hard limits. Make sure your device is set up correctly and can handle these limits before you install. + + * `50A` charge (to change this see [How to change the default limits](#how-to-change-the-default-limits)) + * `60A` discharge (to change this see [How to change the default limits](#how-to-change-the-default-limits)) + * `2.9V` min cell voltage + * `3.45V` max cell voltage + +The cell voltages is used along with the cell count to set the battery voltage (e.g. for 16cells your battery min voltage will be `3.1 * 16 = 49.6V` and max coltage `3.45 * 16 = 55.2V` + + +## Settings for your BMS/battery + +You need to first set up your BMS hardware to match your cells. You would do this, if you build you own battery or your manufacturer/installer have done this for you. +The important steps: + + * Use the same cells (type, branch and capacity) and make sure they are balanced. + * You need to correctly set your battery capacity to match the cells you are using. Your SoC calculation in your BMS will be wrong otherwise. If you use `120Ah` cells then your battery capacity will be `120Ah` etc. + * You need to correctly set your min/max cell protection voltages. These are voltages when your BMS will disconnect to protect your cells like `2.85V` and `3.65V`. Your driver limits should be between these and NOT the same. + +## Settings for your GX device + +1. You need to have a Venus OS device set up and running on your GX system (VenusGX, Cerbo, Raspberry Pi, etc.) and connected to your inverter. +In [VRM](https://vrm.victronenergy.com/) look under the device list for your installation. If you can see the Gateway (GX) and Ve.Bus System (inverter) then your GX is ready. + +2. On your GX device you should set DVCC On. This will enable your battery to request charge parameters. All the Share Sense option can be Off. If your battery works with lower limits, enable Limit Charge Current, Limit managed battery Charge Voltage and set the lower values as required. You can also enable Limit inverter power for Discharge Current limit under ESS. These settings will be remembered between updates. +![DVCC values](../../screenshots/settings-dvcc.png) + +3. You also need to connect your BMS to the Venus OS device using a serial interface. Use the cable for your BMS or a Victron branded USB→RS485 or USB→Ve.Direct (RS232) cable for best compatibility. Most FTDI/FT232R/CH340G USB→serial also works. The FT232R and CH340G already has a driver included in the Venus OS. + + > 🚨 **NB! Only connect Rx & Tx or A & B to the BMS,** if you are NOT using an isolated ([galvanic isolation](https://en.wikipedia.org/wiki/Galvanic_isolation)) cable or adapter. This prevents the current to flow through the adapter, if the BMS cuts the ground. Else it will destroy your BMS, GX device or Raspberry Pi. + +## Install or update + +### Installation video + +[![dbus-serialbattery install](https://img.youtube.com/vi/Juht6XGLcu0/0.jpg)](https://www.youtube.com/watch?v=Juht6XGLcu0) + +### Install automatically with flash drive/SD + +> CerboGX users cannot use the the automatic installer. Use SSH option instead. + +1. Download and copy the [latest release](https://github.com/Louisvdw/dbus-serialbattery/releases) `venus-data.tar.gz` to the root of a USB flash drive that is in FAT32 format (a SD card is also an option for GX devices, but not for Raspberry Pi). + +2. Plug the flash drive/SD into the Venus device and reboot. It will automatically extract and install to the correct locations and try the driver on any connected devices. + +3. Reboot the GX (in the Remote Console go to `Settings` → `General` → `Reboot?`). + + +### Install over SSH + +> Require [root access](https://www.victronenergy.com/live/ccgx:root_access#root_access) + +1. Log into your Venus OS device using a SSH client like [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) or bash. + +2. Run these commands to install or update to the latest release version. + + ```bash + wget -O /tmp/installrelease.sh https://raw.githubusercontent.com/Louisvdw/dbus-serialbattery/master/etc/dbus-serialbattery/installrelease.sh + + bash /tmp/installrelease.sh + + reboot + ``` + + +### Install over SSH: specific version/testing option + +> Require [root access](https://www.victronenergy.com/live/ccgx:root_access#root_access) + +1. Log into your Venus OS device using a SSH client like [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) or bash. + +2. Download [the version you need](https://github.com/Louisvdw/dbus-serialbattery/releases) using the command below replacing the URL with the link to the `venus-data.tar.gz` version you need. + + ```bash + wget REPLACE_WITH_URL_TO_VENUS_DATA_TAR_GZ + ``` + +3. Run this script command to install the update from the same location. + + ```bash + tar -zxf venus-data.tar.gz -C /data + + reboot + ``` + + +### Install over SSH: nightly version/beta test + +> Require [root access](https://www.victronenergy.com/live/ccgx:root_access#root_access) + +> This version is installed directly from the repository and can contain errors. Not recommended for production environments unless you know what you do. + +```bash +wget -O /tmp/install-nightly.sh https://raw.githubusercontent.com/Louisvdw/dbus-serialbattery/jkbms_ble/etc/dbus-serialbattery/install-nightly.sh && bash /tmp/install-nightly.sh +``` + +Select `2` for `jkbms_ble`. + +### BMS specific settings + +* ECS BMS → https://github.com/Louisvdw/dbus-serialbattery/issues/254#issuecomment-1275924313 + + +## How to change the default limits + +The driver currently use a fixed upper current limit for the BMS: + +* `50A` charge +* `60A` discharge + +If you require more current and your battery can handle that, you can make changes to the source code for that (note that any updates will override this change with driver version `<= v0.14.3`) + +```ini +MAX_BATTERY_CURRENT = 50.0 +MAX_BATTERY_DISCHARGE_CURRENT = 60.0 +``` + +If you use the cell voltage limits, temperature limits or SoC limits you also need to adjust their values to match the new current, else CCL and DCL will not change. See also in the [FAQ](../faq/#why-is-the-chargingdischarging-current-limit-ccldcl-smaller-than-the-set-one). + +### Settings location/path + +💡 After updating the settings reboot the device or run `/data/etc/dbus-serialbattery/reinstalllocal.sh` to apply the changes. + +#### Driver version `<= v0.14.3` +Edit `/data/etc/dbus-serialbattery/utils.py` to update the constants. + +#### Driver version `>= v1.0.0` +Copy the values from `/data/etc/dbus-serialbattery/config.default.ini` to `/data/etc/dbus-serialbattery/config.ini` you want to change. All options can also be copied from [here](https://github.com/Louisvdw/dbus-serialbattery/blob/jkbms_ble/etc/dbus-serialbattery/config.default.ini). + +## How to edit `utils.py` or `config.ini` + +There are two ways to edit the files. You can edit them: + +1. Inside the GX device/Raspberry Pi over SSH +2. On your PC and then copy only the `utils.py` or `config.ini` over to the GX device/Raspberry Pi + +### SSH edit using Nano editor (recommended) + +Log into your GX device/Raspberry Pi using SSH and run this command. Replace `FILE_NAME` with the file name you want to edit. + +```bash +nano /data/etc/dbus-serialbattery/FILE_NAME +``` + +You can use the arrow keys to scroll down and edit the values you need. + +Use `Ctrl + O` (O like Oskar) to save and `Ctrl + X` to exit the editor. + +### Copy edited file from PC to GX device/Raspberry Pi + +You can edit the file in a plain text editor on you PC like Notepad (Windows) or TextEdit (macOS). Then you need a program that can do SFTP like [FileZilla](https://filezilla-project.org/download.php?show_all=1) (Windows/macOS/Linux), [WinSCP](https://winscp.net/eng/downloads.php) (Windows) or [Cyberduck](https://cyberduck.io/download/) (macOS). + +Connect to your GX using the same login as with SSH and copy your edited file over the existing one at `/data/etc/dbus-serialbattery/utils.py` or `/data/etc/dbus-serialbattery/config.ini`. + +⚠️ Sometimes it happens, that the line endings get changed from `LF` to `CRLF` with this method. Check the [FAQ --> `$'\r': command not found` or `syntax error: unexpected end of file`](../faq/#r-command-not-found-or-syntax-error-unexpected-end-of-file) to solve. + +> Don't copy all the files as the required file permissions will be destroyed and your driver might fail to start. + +## How to enable a disabled BMS +If your BMS is disabled by default, you have to enable it to get it working. + +💡 See also [How to edit `utils.py` or `config.ini`](#how-to-edit-utilspy-or-configini) if you don't know how to edit a file. + +#### Driver version `<= v0.14.3` +Edit `/data/etc/dbus-serialbattery/utils.py` and uncomment (remove the `#` as first line character) your BMS. + +E.g. + +```python +# {"bms" : "Sinowealth"}, +``` +becomes + +```python + {"bms" : "Sinowealth"}, +``` + +Edit `/data/etc/dbus-serialbattery/dbus-serialbattery.py` and check, if your BMS is already uncommented (without the `#` as first line character) your BMS. + +#### Driver version `>= v1.0.0` +Edit `/data/etc/dbus-serialbattery/dbus-serialbattery.py` and uncommented (without the `#` as first line character) your BMS twice (`# from ...` and `# {"bms": ...}`). + + +## Disable the driver +You can disable the driver so that it will not be run by the GX device. To do that run the following command in SSH. + +```bash +bash /data/etc/dbus-serialbattery/disabledriver.sh +``` + +You also need to configure your MPPTs to run in `Stand alone mode` again. Follow the Victron guide for [Err 67 - BMS Connection lost](https://www.victronenergy.com/live/mppt-error-codes#err_67_-_bms_connection_lost). + +## Enable the driver +To enable the driver again you can run the installer. + +```bash +bash /data/etc/dbus-serialbattery/reinstalllocal.sh +``` + +## Uninstall/remove the driver + +To uninstall/remove the driver run the uninstall script. The script is included from driver version `> v0.14.3`. + +```bash +bash /data/etc/dbus-serialbattery/uninstall.sh +``` + +To uninstall/remove previous driver versions `<= v0.14.3` run this commands. + +```bash +# handle read only mounts +sh /opt/victronenergy/swupdate-scripts/remount-rw.sh + +# remove files, don't use variables here, since on an error the whole /opt/victronenergy gets deleted +rm -rf /data/conf/serial-starter.d +rm -rf /opt/victronenergy/service/dbus-serialbattery +rm -rf /opt/victronenergy/service-templates/dbus-serialbattery +rm -rf /opt/victronenergy/dbus-serialbattery + +# kill if running +pkill -f "python .*/dbus-serialbattery.py" + +# remove install-script from rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local +``` + +> If after the uninstall for some reason several items in the GUI were red, DO NOT reboot your GX device. See [Uninstalling driver bricked my cerbo #576](https://github.com/Louisvdw/dbus-serialbattery/issues/576) diff --git a/docs/docs/general/supported-bms.md b/docs/docs/general/supported-bms.md new file mode 100644 index 00000000..416e7362 --- /dev/null +++ b/docs/docs/general/supported-bms.md @@ -0,0 +1,68 @@ +--- +id: supported-bms +title: Supported BMS +sidebar_position: 3 +# Display h2 to h4 headings +toc_min_heading_level: 2 +toc_max_heading_level: 4 +--- + +# Supported BMS + +## Currently supported +### • ANT BMS +Disabled by default since driver version `v1.0.0` as it causes other issues. More informations can be found in [Add other use case (grid meter) or ignore devices - ANT BMS check missing](https://github.com/Louisvdw/dbus-serialbattery/issues/479) and if it was fixed. See [How to enable a disabled BMS](../general/install#how-to-enable-a-disabled-bms) to enable the BMS. + +### • Daly Smart BMS +Including: +#### - Sinowealth based Daly BMS +Disabled by default since driver version `v0.14.0` as it causes other issues. See [How to enable a disabled BMS](../general/install#how-to-enable-a-disabled-bms) to enable the BMS. + +![Daly app](../../screenshots/bms-daly.jpg) + +### • ECS GreenMeter with LiPro + +### • HLPdataBMS4S + +### • [JKBMS](https://www.jkbms.com/products/) / Heltec BMS + +### • Life/Tian Power +Including: + +#### - Revov + +### • MNB spi BMS +Disabled by default as it requires additional manual steps to install. + +### • Renogy BMS + +### • Seplos +So far only tested on version `16E`. + +### • Smart BMS +Including: +#### - [LLT Power](https://www.lithiumbatterypcb.com/product-instructionev-battery-pcb-boardev-battery-pcb-board/ev-battery-pcb-board/smart-bms-of-power-battery/) +#### - [Jiabaida JDB BMS](https://dgjbd.en.alibaba.com/) +#### - Overkill Solar +#### - Other BMS that use the Xiaoxiang phone app + +| Android | iOS | +|-|-| +| ![Xiaoxian app](../../screenshots/bms-xiaoxian-android.jpg) | ![Xiaoxian app](../../screenshots/bms-xiaoxian-ios.jpg) | + +## Planned support + +You can view the current [BMS requests](https://github.com/Louisvdw/dbus-serialbattery/discussions/categories/new-bms-requests) to see which BMS support is requested and vote for the BMS you want to be supported. + +## Add/Request new BMS +There are two possibilities to add a new BMS. + +1. Fork the repository and use the [`battery_template.py`](https://github.com/mr-manuel/venus-os_dbus-serialbattery/blob/master/etc/dbus-serialbattery/bms/battery_template.py) as template to add a new battery. As soon as the BMS works you can open a PR (pull request) to merge it. + +2. Start a [new discussion](https://github.com/Louisvdw/dbus-serialbattery/discussions/new?category=new-bms-requests) in the `New BMS request` category. Please add also the protocol documentation which you can request from the manufacturer/seller. The more upvotes the BMS request has, the higher is the priority. + +If you would like to donate hardware or would like to help testing a specific BMS please get in contact over the [discussions section](https://github.com/Louisvdw/dbus-serialbattery/discussions). + + +## Which BMS are you using? +Please let us know, which BMS you are using with the driver by upvoting your BMS: [Which BMS are you using?](https://github.com/Louisvdw/dbus-serialbattery/discussions/546) diff --git a/docs/docs/troubleshoot/faq.md b/docs/docs/troubleshoot/faq.md new file mode 100644 index 00000000..b27132c0 --- /dev/null +++ b/docs/docs/troubleshoot/faq.md @@ -0,0 +1,13 @@ +--- +id: faq +title: FAQ +sidebar_class_name: hidden +--- + + + + + +# FAQ (Frequently Asked Questions) + +This page has been moved to [here](../faq/). diff --git a/docs/docs/troubleshoot/index.md b/docs/docs/troubleshoot/index.md new file mode 100644 index 00000000..b344c7ac --- /dev/null +++ b/docs/docs/troubleshoot/index.md @@ -0,0 +1,176 @@ +--- +id: troubleshoot +title: How to troubleshoot +sidebar_position: 2 +# Display h2 to h4 headings +toc_min_heading_level: 2 +toc_max_heading_level: 4 +--- + +# How to troubleshoot + +## Driver log files + +> Require [root access](https://www.victronenergy.com/live/ccgx:root_access#root_access) + +Check the log files on your GX device/Raspberry Pi. Connect to your Venus OS device using a SSH client like [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) or bash. + + +### Serial BMS connection + +#### `/data/log/serial-starter/current` + +Serial starter will show, if the driver was started against a USB port. + +**Execute** +```bash +tail -n 100 -f /data/log/serial-starter/current | grep dbus-serialbattery | tai64nlocal +``` + +**Output** +```bash +... +INFO: Create daemontools service dbus-serialbattery.ttyUSB0 +INFO: Start service dbus-serialbattery.ttyUSB0 once +... +``` + +✅ This indicates, that the driver was successfully started against the `USB0` port. + +#### `/data/log/dbus-serialbattery.ttyUSB*/current` or `/data/log/dbus-serialbattery.ttyAMA0/current` +Where `*` is the number of your USB port (e.g. `ttyUSB0`, `ttyUSB1`, `ttyUSB2`, ...) or `ttyAMA0`, if you are using a Raspberry Pi hat. + +**Execute** +```bash +tail -n 100 -f /data/log/dbus-serialbattery.ttyUSB0/current | tai64nlocal +``` + +**Output** +```bash +... +INFO:SerialBattery:Starting dbus-serialbattery +INFO:SerialBattery:dbus-serialbattery v1.0.0 +INFO:SerialBattery:Testing BMS_NAME +... +INFO:SerialBattery:Connection established to BMS_NAME +INFO:SerialBattery:Battery BMS_NAME connected to dbus from /dev/ttyUSB0 +... +INFO:SerialBattery:DeviceInstance = 1 +INFO:SerialBattery:com.victronenergy.battery.ttyUSB0 +... +``` +✅ This indicates, that your driver started successfully and connected to your BMS. You can see now the BMS in the GUI. + +❌ If you see an error like below, then your battery is most likely connected to a different `ttyUSB` port. + +```bash +ERROR:SerialBattery:ERROR >>> No battery connection at /dev/ttyUSB0 +``` + + +### Bluetooth BMS connection + +#### `/data/log/dbus-blebattery.*/current` +When you are using a Bluetooth connection `*` is the MAC address of your BMS. + +**Execute** +```bash +tail -n 100 -f /data/log/dbus-blebattery.*/current | tai64nlocal +``` + +**Output** +```bash +... +INFO:SerialBattery:dbus-serialbattery v1.0.0 +INFO:SerialBattery:init of BMS_NAME at BMS_MAC_ADDRESS +INFO:SerialBattery:test of BMS_NAME at BMS_MAC_ADDRESS +INFO:SerialBattery:BMS_NAME found! +INFO:SerialBattery:Connection established to BMS_NAME +INFO:SerialBattery:Battery BMS_NAME connected to dbus from BMS_MAC_ADDRESS +... +INFO:SerialBattery:DeviceInstance = 1 +INFO:SerialBattery:com.victronenergy.battery.BMS_MAC_ADDRESS +... +``` +✅ This indicates, that your driver started successfully and connected to your BMS. You can see now the BMS in the GUI. + +❌ If you see an error like below, then your battery is not found. + +```bash +INFO:SerialBattery:Starting dbus-serialbattery +INFO:SerialBattery:dbus-serialbattery v1.0.0-jkbms_ble +INFO:SerialBattery:init of BMS_NAME at BMS_MAC_ADDRESS +INFO:SerialBattery:test of BMS_NAME at BMS_MAC_ADDRESS +ERROR:SerialBattery:no BMS found at BMS_MAC_ADDRESS +ERROR:SerialBattery:ERROR >>> No battery connection at BMS_NAME +``` + + +### What to check, if it doesn't work + +The log file will tell you what the driver did and where it failed. + + +#### No log file +If there is no log folder under `/data/log/dbus-serialbattery.*` then check: + + * Did the install have any error? Reinstall the driver, also trying an alternative method. + + * Is the connection picked up by serial-starter? Use the command + + ```bash + tail -f /data/log/serial-starter/current | tai64nlocal + ``` + + to show the last part of the log file as it updates. Plug your USB device in and out to see, if it's picked up and what `tty` port it uses. + + * Check, if your BMS type is found (change to the `ttyUSB*` your device use) + + ```bash + tail -f /data/log/dbus-serialbattery.ttyUSB0/current | tai64nlocal + ``` + + or + + ```bash + tail -f /data/log/dbus-serialbattery.*/current | tai64nlocal + ``` + + to check all devices the serialstarter started. + +#### `No reply` in log file + +Check your cable connections, if the log file shows `ERROR: No reply - returning` from the battery. + +The RX/TX lights should both flash as data is transfered. If only one flashes then your RX/TX might be swapped. + +## FAQ (Frequently Asked Questions) + +Check the [FAQ (Frequently Asked Questions)](../faq) for answers + +## Alarm logs (VRM Portal) + +Check your Alarm Logs in your [VRM portal](https://vrm.victronenergy.com/installation-overview) after selecting your device. + +## Advanced section (VRM Portal) + +Check your graphs in Advanced section in your [VRM Portal](https://vrm.victronenergy.com/installation-overview) after selectiong your device. + +You can use the graphs to look at your values over time. This makes finding values that change much easier. + +* Battery SOC (State Of Charge) +* Battery Summary +* Battery Temperature Sensor +* Battery Voltage and Current +* BMS Charge and Discharge Limits +* BMS Min/Max Cell Voltage + + +## Forum and community help + +Forum thead discussions for this driver can be found at: + +* [GitHub Discussions](https://github.com/Louisvdw/dbus-serialbattery/discussions) (most frequented and recommended) +* [Energy Talk - DIY Serial battery driver for Victron GX](https://energytalk.co.za/t/diy-serial-battery-driver-for-victron-gx/80) +* [Victron Community - Victron VenusOS driver for serial battery BMS](https://community.victronenergy.com/questions/76159/victron-venusos-driver-for-serial-connected-bms-av.html) +* [DIY Solar Power Forum - Victron VenusOS driver for serial connected BMS](https://diysolarforum.com/threads/victron-venusos-driver-for-serial-connected-bms-available-ltt-power-jbd-battery-overkill-solar-smart-bms.17847/) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js new file mode 100644 index 00000000..8139d884 --- /dev/null +++ b/docs/docusaurus.config.js @@ -0,0 +1,141 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion +const lightCodeTheme = require('prism-react-renderer/themes/github'); +const darkCodeTheme = require('prism-react-renderer/themes/dracula'); + +const organizationName = "louisvdw"; +const projectName = "dbus-serialbattery"; + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'dbus-serialbattery', + tagline: 'Venus OS battery driver', + favicon: 'img/favicon.ico', + + // Set the production url of your site here + url: `https://${organizationName}.github.io`, + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: `/${projectName}/`, + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: organizationName, // Usually your GitHub org/user name. + projectName: projectName, // Usually your repo name. + + //onBrokenLinks: 'throw', + onBrokenLinks: 'warn', + onBrokenMarkdownLinks: 'warn', + + // Even if you don't use internalization, you can use this field to set useful + // metadata like html lang. For example, if your site is Chinese, you may want + // to replace "en" with "zh-Hans". + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + routeBasePath: '/', + sidebarPath: require.resolve('./sidebars.js'), + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: 'https://github.com/Louisvdw/dbus-serialbattery/tree/docusaurus/docs/', + sidebarCollapsible: false + }, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + algolia: { + appId: 'BUS7YVLUUB', + apiKey: '11f8f0c4ceaf5dd684a254191cc007d6', + indexName: 'dbus-serialbattery', + }, + // + colorMode: { + //defaultMode: 'light', + //disableSwitch: false, + respectPrefersColorScheme: true, + }, + // Replace with your project's social card + image: 'img/docusaurus-social-card.jpg', + navbar: { + title: projectName, + logo: { + alt: `${projectName} Logo`, + src: 'img/logo.svg', + }, + items: [{ + to: '/', + activeBasePath: 'docs', + label: 'Docs', + position: 'left', + }, + { + to: '/faq', + activeBasePath: 'docs', + label: 'FAQ', + position: 'left', + }, + { + label: 'GitHub Issues', + href: `https://github.com/${organizationName}/${projectName}/issues?q=is%3Aissue`, + position: 'left', + }, + { + label: 'GitHub Discussions', + href: `https://github.com/${organizationName}/${projectName}/discussions?discussions_q=`, + position: 'left', + }, + { + label: 'GitHub', + href: `https://github.com/${organizationName}/${projectName}`, + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [{ + title: 'Community', + items: [{ + label: 'GitHub', + href: `https://github.com/${organizationName}/${projectName}`, + }, + { + label: 'GitHub Issues', + href: `https://github.com/${organizationName}/${projectName}/issues?q=is%3Aissue`, + }, + { + label: 'GitHub Discussions', + href: `https://github.com/${organizationName}/${projectName}/discussions?discussions_q=`, + }, + ], + }, ], + copyright: `Copyright © ${new Date().getFullYear()} ${organizationName}`, + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + }, + }), + + scripts: [{ + src: `/${projectName}/matomo.js`, + async: true + }], +}; + +module.exports = config; diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..fbecf0e0 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,46 @@ +{ + "name": "docs", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids", + "typecheck": "tsc" + }, + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/preset-classic": "2.4.0", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "prism-react-renderer": "^1.3.5", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "2.4.0", + "@tsconfig/docusaurus": "^1.0.5", + "typescript": "^4.7.4" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "engines": { + "node": ">=16.14" + } +} diff --git a/docs/screenshots/bms-daly.jpg b/docs/screenshots/bms-daly.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65ae954f166c630a8380a7e5e54622c243e34523 GIT binary patch literal 118733 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/bms-xiaoxian-android.jpg b/docs/screenshots/bms-xiaoxian-android.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b3aa9286fc29cb07ed126bb6096caf5d219299cc GIT binary patch literal 74310 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 E0mOA582|tP literal 0 HcmV?d00001 diff --git a/docs/screenshots/bms-xiaoxian-ios.jpg b/docs/screenshots/bms-xiaoxian-ios.jpg new file mode 100644 index 0000000000000000000000000000000000000000..376a4ac6b59c69c36bf691480a0dfc32427b3c3d GIT binary patch literal 98670 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

)TFt2^D*mJ$%

F;5bf3D89dAdMd3<8I!Vpnd*%e&W~>q*qgw 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

q{VU&JG)b?{ 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

  • OJ)l^h83sg4w=m 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 T*8l(cf6jgXlh4EHm)?H^xj-mz literal 0 HcmV?d00001 diff --git a/docs/screenshots/settings-dvcc.png b/docs/screenshots/settings-dvcc.png new file mode 100644 index 0000000000000000000000000000000000000000..563ea7a086fb394f82ff178ddf70b3d384b90ef1 GIT binary patch literal 37610 zcmb@uWmFtp(=N;mg9jKq2>}Lo4-kAH0)apX?oJ5qZi55}?oN>4F2SAP5)#~<5Zv9q z=1%VSeV((<_wTILET*TYcURS}y?0$#)$UJnvJ%*sk1>&ukg#7%zK};kLWUtBfeg@5 zfp0pgt$dM?U`VfDh$%X2Z>ORq5Oz*~6m4&hT8XMOBzl9M_Yle*NR_D%V_<4Ye^{zd zOhkmt5U3A_zrz!wVP?`M2n30t&`PS?s&ZkmTsvTzS?+Og&j<@=xSft3IiKv>t2h_B z=E!d!=DJVt+ z(tjQ(=o9~Sju8QGM+cgHz(Y#)-_52YEj|9v#{i!tAA&F={4(A@r-6h2^B{z2BLC+( z@EL;|4vOL!H4pr6B@kdgv;R{sJwSeZ|0L|l{}xORVU7LYvSZMMAfZUM%T-eTm;Nvi zCbZ-^MV?}To^+~C#>$z1O zGX1NKx4Rh7X*zGX(O!eA(KQ|288;@MSvBcOBVt&zHHXJUS^n3^y0m~QCx9xtwAKs! zYgw;5d)}C1L>#X5kL(1Q1^;XHMFv1QO+w&4Ww1;kVXWKC3tr7hd>9e4B9u`MVgLGn zIs--mn2`+~kcHWM*#yOK(#VKfyqq4+^h-~I(&>Lie0?vXtRR{U7!M<)vEm8fQ0V_U z7~CCjrH%k*Gg^=+K|0dG(RTUXUy&DW071ypV2Iy-%qA-bND)I73BGr564^8zB1LcWk#; ze{XB1hl7@{7*~-bVZQ+9(hrLviu%{N9^M-S3y`Yqe%R3cPhTzH4;af2YMdD7alkXJ za>K~z|2oy)y)L&;$^&R3J^*)rqWgaO_cj;>;AXuN(SsRL2pQlKuh;?Czor7M0}n{y zULm82*aSYwd-(H<{Jkbg1?WQ4ZahM>_zt*>JoTaDzda`J4QRzQK-CQgmjhjlukpDk z^xw{G6$ge-zc)k@mVlr~>B*5vex~r(ym0Vqs))!)yvjI6xW0cBQ6#(%x?hB3GLoUu z2mb~CD{d=0q&ZPYiUEGjrLj$qaUKmQD!LxpME6Hg?^9Nvh2UWi64i zw3qaYh&4(OUo^Y`KTo01KZZ$79G89l2W9VkJZo~bc`TWr6B*BH7j{_W*RSmrZbRg1 z&H5+r()diALkQV4RULPyS(%lKdrw?^4K^ixT^)|sx5lkv7H(B(uFfc)72q^XWN*4# zYu-{u#KqCk^N1=ZkD5mDgPxLvKa<{7K;4~~WGQyRPOa-Ioi!P1J2sSx?-)*e0I)q!!= zu*Xjz9eKEPJqftff}R$tJwImwoqph*D(!(P_$8OPk?$ z6YOf$;w8-+j;xa6ME|LpJU9#P4YobA;L9h-G~ra%vX!nfezdd%_1Ycm&ePnaGY%x|&3j-)b~u0I^(6U}QWjee|DD7mHZHijOxDU;9Cu~d3a zZL#NCGF|Vcp6mAknqeD7aJFrBknL__?uOnAd{~+5!1;%)EE4H>@jDTNpXd>1B1trE zXFL5LcLsk_m~PSb4bGKB-p$$s-|neZwu~r;pOSoyH7niDyE|?3V?11p9t!A?1ub`v z^@v0xTF|pXklIKtzTDMO@2}PCjlADD^*TeBb-fP|+6g$oklYOVG`sv}=y*u{XIDvC zbH}3FJ13FzEO!BqI1!~AyA8lwyHS^wRO7OpDUe0dsxz$zaD4{tWgg39rNFp46b!w$AnhpWGsFO(}#Fb!O6 z8|0_sJUTTpmc}|c6Lzx;emMFPv^=KNT3 zBHl*8P!4t)5Ni~JnTimuI;HhFOH>pcClfAmU3kb!)hLqIosQT#`qv2Ns#7#3tH zasvoi;9*wNk|Hi=4mVS^Z*%&P-#!?=vmk_1bQD&NNln9L0?$P=@(Hmfyfqki;q%h8~t`N$X~Rm^lbETPY7E?aMWg z_)kkBlb)Al@l zSGU?|ckFd`Gi1QEb2KFpnhfq3lV2PJ)&XAhb8SS3he6p_p$mxcC~g1kJ8uUkKkTB) zHsAONcr!KhHRAeuELXnzjfvXjkW}4bkR^zBQnKxiY*$%pjQi|nAUs?Dqb-+97s+tx z9g>GuAu;{tIWD2$`J?OUo(S_D?f5-9f8g0VM%oNMrjv0YZ z8`O`{&GU0*q0FYz^Hxs%v#pQj)PerRuK<^*I>)AxFMES?S)Oy#mv$?}Q<`&vddJo` zd>wGd`V1$;+F~vRjx*N7i9X#Qgs^6HS3E(%W@FaG2HWrlm-=?{sAC7`!?qs56XXxlfb z>W5Q)KQD>1#)WS~f+6`;E$6F&oi_QGT8-&;XYkmbOcWKio5s_+x3`7Eho~f&PaOdRSSra(=M5A$xsfM7vYh1B!0N4}jMkI@$~ zKu<+)4>i&&q==Y%YPdy))!ZXKp_N2PUjO)&$u?cp5fk}5Et>nSpwis}Xp;Zf&S%%xo20W_d32lN+!zMS1iGR>*6e=UbCF*BbQ7F`ZR{o zS~^OHs2S}~;gcpL5Qn+`XP0AtjJ#NJVQAA_|MEREA~UY)|D)<#o93TH+jsm>kk9Ejp;Oi zE_xPEvq%f>VN%MmXPnF4W#UAZfA>Y`b{m>lROHW4)>9)?SNFg*)2pOpg`XQN>UQTz z1`kuse?1}+Z!PxwX75te1IlqVQ>SjeShMHtStE5;lIk$$#d52`w&O}eFmZ%@iRMW_ z;eLp+cFLoMmPU99FA?fR>1*9jcVOaG^n+0wD2cND}@s%&~%VyF#ft|tUSz8RP( zfhx)gSDp_+hrK?jw%_uNvR@@WWsKF9tH33XThlo0|4mV8*qgKc<+0D&a)@hZw&h2v z%gWR%oZh4zwAGOXrEwl21$@mEb$I<}O=STdcGi)*Sw^ct{-`GG?3X*swi`CtS)Wa7 zJ}iOj=S2Co362+z?F=U#SGb&s)j7$3aO$K~QH-^B*kh$2@AccXXW7lKL=}X{ze4Di zo$K#7nT5A95*~i0cKA@st|o|Z#s-|m937^`%lq;p7D<%D7T?AjwG6;6`XkKa;^3bI zl3$TFGFZs81f53rX3B{>T!gq+c}_Bi`0H+Q_#_%_$V@6^#Nt@c@AiJ(K5<+mg#9eV zD4oO5vfVR)_sVd zKVP}TB8`tSm1=Dib}Dqw*c7Wfn++P4|Kz_oe6gc+`8GqdOipDeZmkNbqT1rNS^30d z_L{6dt@ds?ht?c^i3+FS11$Y2gD|bIw{M-uaJ>hUS}wuXY30X81+d;`!H}{fYGD6Ofj_G8rqz z93N*^c&_9K8IbE)w4GR^X=H1{j+h8xE_jDS>rfa&W&Cbj#@*nJE4VU}Prr?c!QZQf z7RFWdg=g7te?kMCd?#r`-j>GG!@zX*wK%=-DKVyk*Ry_9zt?*s%#ar-o6g&rQe2{T zd+(;2$OS~7Cn>&;@6NFrZSO^=*?M@x?Up|`5<>_};L2ih-^^&pmdg-7JPt1H)7T?- zSt0;N{vRL!cLsn;ex(2mzSuQtu$}Y;l}PGYqto-jFDwM(q=UZ}Oq;B!;HVBonZ*pR zU~~vxn`KwQ;$RCINS{obffFh# zmvE&!)x)6lY|-CHvykq(`gzxFN;W$;Ro7;c5rja5;%v*YIhaOvizUfaK^^e|+3)OM zr99~XT1B9e0vCk8J#Hw0fW6!+x#6FVWr`7e$ns;#IiC4k0;wzh`&Fs z7~w6oxrgz>&&U6OJvf;0IRZ>$lp~*l4h@;Md=SX<9c(F!ET~w0L)AV}T95FtoYphiT6+8U5V zq^g*tEIUCV7MQSTXUE!sC$--o+g@ju{e8gAi-r(3pGJKe@fUdirF~ZBBNRYBIEjkY zj`PU+sJgotILA076#C1OMWBt^>i$ngkUdahxwA10n`3_FXN4>$E!8PStkk5c&W~`p zJrr-<0>z4m>sni@#g7564A<67iO+g_*Rkagt4AoUOMd|@A{OwS&7y!05cF3RgEQYk zdB9fce8e%5pv?@;@;52|E(U^^K8tpN;nv~jP?pJN3ocSMLzp`Ex4?`8>VWD&$32RhL4bTbBZl05Z{}LJuK(l_!?+tJ; zT8qtw+sUS79&l0zDm}VfV8t<8TI;=c+wDiXSAXe*Map~HBk6(T8Ur|xOiW1PwCx)( zGrOajM>lLTVf4yXs2M2Fu@+tj%4Y2gQF6` zb`KllcFIE61m4)Ym2V4f1qlOoo#ZwO1?*Obk>!WPsGSv|X>IPppRfG7mqFI ztP7mDXo^;cZJtiUk)lbg>DOqV?VDGeGRVo3@hzIHf@f>1nIqLU{QNr!T$fgrchhtW zY;)x2*CCfitJ|;Ej_>X~ryS?LYpstF9@Caq*sOa#I#{T}&-WaCCuUJLO`#(eR;VrC zN5am6W{cDRxN7QBtcveykENGCOTdzG{PL7Sh{o5rIPPYXT;a{#>Gn`0iKtGh3Sao; zb zH}vq-Y0GeywYxxJ>Bmi(a-(g(NZs083yu7ZF)su2S&EKA5Fy(`TTL&kN$PMJZAuN+ z1{JWbj$??ari=Tz%uN`-+FjQP826vC4#xSq(mC=P+ik&6t$rqt4r@ zN6fL3{ZhY=^d%6wVL$d3WlV*5yD-Qa(H>V;nuPKgt15QQk1Eb5H;B=FU!Z}uL4SF& zx0yXcqbLq%WMy~%b)62w54Ik9-Z*`9)wnuwtAJRYNL;Ud4YPr`v%*c|n3>phM&vB~ z!p(=v8PhJhm8YuL(<}V4UT?V?t)^Gt3jvHVn{w`XO_~NZ$y&=O#swJ#n++}8lD|^D z6)Z33u+d1j=HL+!^J(o!v<79^%6D6i$ed@U6aL`% z1ha)}ZX0Hmsu!RrxmgLGxCfx&EK>}Zd})u3quYgwrx?mL&UiJIaRxsfpMmg$>(wMS z0mAWhYTc@&WNYEQqU6n4VJRh=8FHQqyUI88Y@&Uv`KF0f6_!`Gn%6ptqzO5u)W|km zUMFncW_Upk2BCL*qhNgtNaly-JF?O{KZ0qT=Oj<6P}|4uMvYEZSG}&DIxG>pto8GF zyGa*d-}UUue0?b9qNyh4sp{Fm!=j2qY$d#^0r}*)q?U`V|7R@K zvPGt|m7vta&-lh}xKH}V!WFjS#))&8vV(indD6ZpX{n^d;Qd6wy4fk(EX~uLdeflh zX1khi!rYNEu)yb;KU+{F5XwXn&nop$HmOA|=cbufWyY}!A5;D6JyDE?F5}#U#!vy< z2{X}%MXNgRSsIzG_Q;{@wb5AqZQ9mpPXxQN*@1bGmPk%&WWl_ofwwM#!|NjWu9*Jo z$L1X+s<9%1h8+Ps*fR(MUYa{gUOD=Z{<{8kPJWdItBZ|)!-QIBpHx#&=hyy)W@P@p z8>wer5lP9B9!N!%>dSX@pR394ycTPd?emm}%&Hi;#_NbU5U=KPb)8V0OO zQ>)xdY;lDoZVYhVZhzOhz&9Db`AR)R)=XW!H~15c+q%2M73)k0Qe*5SqbKz2cU_6n zT5IK}Z?Oc5S7@VW%;%No;lPUWC)jDh+b>4)z8dXr3ZF>Uh?x;p&Gh#MQs$lKj(!fqg}SCpxj$iFw(~TMbzLEPEt z7rOhwb03OW#G#=rMbp`Q#n#{IN{ozdF1TpT+dC0Y)YzlnkTwRdiy9ASPp+i;M zs|y^BS*PjDs&}GC!Wj#fUHkfwrFw4bO>;{Yc2&hWvA$;bjoEiPu7us#e!{1nWbk@s z_2p}xf)w3d^-GQLGLsHfSf)&QfA_BHZbN$9sA9+!)0TJDgH87k;xK3>gX2sUGcZlq zylGH$AER00s*)=H#Jl+eDS1hZTcwwT$+3^kSXETLfXCh;SnBZm*_M6EglUyB>{aWr z8WF+qS(mW-i=0AagxT0^E)f&M-PybjNY7YxXF!BOC(lJEe^+9gM3ZN3pi1k#KW*uAz~bq4N&ZSfe1w?F=)^)c_wBPob4Mj{ zlJObIq4$&Qk31JKfRN*J0;;1AL_P^&g@P`-yU@547}1(@HmoPX+u}l$lDX1NyvAPz zx-7*^zlC@DwpJibi(#psVeIu;m|zYNSXcwB`)pmx$>RY+o@w-YNNa?zq`x`-*(9T^t-cfGC0<8ux-*qn@1>o}6Y84K8x1RKnYIP%TBMx!gJN=xHXhU$s}qA<%L`Q|s!LN6%jjn1GXZk$ z+gz0mabNIYlgDp&N8VI+>51pz@4BGV!ncJe7Q_ZVr;9_Gw)9l7%E*seN! zx?iT9f0;)@4oN`^m=OX*BWqP=q#GCQbZqi;b$9L4(`0LO zL^TJ8JlMzY+aCLA7fFhlXlDC0Q|yYBiTE+UkA8A}5rd*(IH_WGj0&-6KiOuks>5Rt&uCQNDd|3rr#gTRj$EcOauHy4FYHt?#(8inZgwEwg*`HH~H zx@&-gq-UN*ht@VM-8`~BsZ~~sE=zQmOg%JE2qo||FVhRfG>T}C+Z*;;(SxoZN?r*} z$G*?EP4#~0Cr##aHi%J`xE`Mq3J6i*-7S@B_AJO%PI|?V4d@t|NgVKisdT;CG$^Jx&ziPw=b=HAZ0#+FdT}a&K=Dey(7 z&+21}yNY++ed*8eU@pQGvRx~yZvGQ=X#iX~Tt^7QycOveH9$U}Tuv4Sd2X+$iG*&( zYa)@!_moX6WZoB86C@YQ;+87!`6Et1k8+{u!9H&k$72%4>;0L}=sE;crN4q94y{!W z*xoZdk2L}jqnee6ktVCATxSBNG#3cjdLVmYH+t79^UW00#+dWzOPjYf4PUh!C(Idd z_D2b}z0|z60rA$Q=)2sXP^V`fLlrq>RW&~+R8Uzz(yV#KQ1gfyE_b&o(rvbdrf*+_ zGEuAo+Bm?61yYv@#>uVN1D4tsYj8wCA7|-cn2j$&;ef$w_O=#^tmOIaS& z4rS~ppGa*@GS`l^r4F&J%Yi!v6!s}W(=PILdu`Zw<%*H2b*W~811Uvoy48;B-v^lW zIjt9Mmu#FxhYaFitZt(!nqsO z$1u3?84*NBr$kdy4*Ki?330Rw6Of*AC?j0#m?A7h`t%Yi;Gut-Kd@bLZ~R|uqXK?rk2=U^DI?cdW{;pus5Ia?Vf?aeC%1iUD9Ha%Y0f!(G5E(o0rEFnH?QtK)T2A z3Tj%ibH8HxPE98Y8a9fnua}NuiRr2NVHwVJ8D%fj@tinyI`wIB16@#zB-Vmjz;jY8 z^~;+EIXj4GxR)I!6%o7t&f&wiWlTDZDwA-@NNe+mkfs7;L&6?~HhT zK4NZDV?a}o&XmS!8rgHQ1a=k*rfW}&MJ=$P$Nk?`#f8r;Q3C(zipxCTvauwv0{Y3% zJ|&zDM6rqU&iy>CJ0gs_C;m*L_RPgoQp7mRT1p zC9@@qo1nY*f+k)DKSjFy22prJwj2%3oH#@Y&8rLY2cZ>GlvOOhI+AQK4G`G{{>woX zre=<+@vQDRH3E=8E4g+K`GRTZvU2V?=dW9S;d7@4DT^P&oW;^?7O00aCqyDxffL zYORFT`nXoLW;$fr+FiYCT1O4Q`pv!UHl2S?-tiIA-R0u&WLw1&@J3BMfUi0LI6r)= z_M)_V7S$Xdk^KA1&K96?xrD2QSxEdK6Ta)CBJ~wZe&yhBvcA4gwV5|)xLYKmsJVl( zAG#+Kn1O~$_7C0zlpZNS*_p$aY46#?2z0PyD?+u}DEX8uHlH1DM`8qZb{-mza>mh- za^0j?YO4^MdS&+1V_A3?P^osMy8&SS4(|y_K58H(`Gi5{PItGG!de}!&bvx zelfg;B?6bCUZ+|7%jk9kmz>STC&}<7#efDFpXEQn|NF2P(!j&jZ5zWOlCAeuYzQr?TdivQ^@qtkr1U<{{76GhP5qvFkHh`-z^4Aa zF!^GwmEbJ(lj(f1gfIWrZ&Ki?Cf7xQJ0y-eOQH}IK!|s4Gd^kjmn{t>09eLn-bPix z?I7Txnh*Xe^&hAI)tf+vy1xJ@eu?P&=HA{nXTLmY@lSK!9r(}O&#lwjz$lnB)dw8K zonPMG126S|fEQqOskiKC6906=qCaA4(e0)6Do>p1aXgrYz3(~&umdY#hZ=~B=s$LV zOX0V^pGObx+&b%-^>XZ4AU*Agn?qY|ZTog8>LzLHk4ySSeF-dI{vzpVo~EYy1T!o4 zSj)R252tBCOlWS0voJ;kLIF@p_S1pu9|2?Nb$Rz&il=C;JS&y&>BwNt=ZIrd1(~o? zZ7NedanN#b{(Jh@trI{ux(j=33;hv92>prtcsO5+8a;v6T#UVRz{mlmQ1;R5*sS#p zrC5~ijX5ep*!RQ(9N;O&z^y3<%VU4+$oQsJRpfJOxm5F|S+emfOv;~`vL^fudXG%h ztNavM5_3NO{H2AqI#ktQbh@B+I1-dv*CF3Px3>EE$c(?*4{)CM(^Gi(l4^h&jDl59 z9{wW`f&l!=J>*ueDdfh+H(F!G)gl#h!`8Ur@o=8hr@$Q+?HUd3l&V+C7IP%EB^p~y zwuAYxWS^h_{Vz+;43sv?-7{qG2NEc)nlExbOF}S-*|jyfxVTn32;Rn=?#xIhav6c6 zuO|({U%!8!(|FKDN=bkFBjfq-7YSVc-P)DadAB`m%}Uo3(?BLJcK8ifRI*UHIBfakVm+-N_+$Sf z5gxZy+M?|xVlqs#F3D+<>CcYwI6bx7hG0%2IP}dt%l*oqq0yIZ^cGR2#%IppkyG~ zJbMS!txC3XddEOv!NjalU7@6CsJPOd#WnLx6kgZm)y}i_BQh06{lxjo#Yxq+ji=LQ z65c*P5(FFyiZp6uHRORVVWCg8k4mxaV+0C@2`w(l;!H+zO!S6xWJo0@1U^&F15@Jt ztOy00RaUHZy$cq`vkyxMZ`0ab-*6W_O}tX@S$w;{46gOyy` ze=Q;heuWU?y`V^}be9mH!I_x!AQ_FR4()H)>@Rw$v34<}yd^OGlz|#o+k6N+_&>yu z!h+W7I_%D>b%j%~t7zJMTwPt2+?lR&NaqJn%6R{Vo1jqNcuM|rZ6mp?d|sLtyq-$2pz^qYGY0m*^C$*m~#5fCU`SZ zI*8ugD-UG`GqS=(ZjKc;Cd(0fkgg%~XB~A=l-t90+3U&y*4e;AV4d|=#gjAvaRIY8D^>IPGM33qb@BUZ z!(M!o$+C>^5e|1ZXIoyMy>4B^Fts|o(_ysi;Gat+JbLYZfkmmBq=l~yg#S1}IrZX8 z5SJQ8#8V)9CBtO~>@3(P^o}8hkqOxUC{gY~7!B$>{7N+L14@`z&lbEUK);-=(7CkK z`lpQ3ibZuA9E%AV@%ilnqs@eQuC^cinSfiF7HLU}BKuGZO&@5LZ8DJ*E!np%n*vkW zKDu`d5zL?~B$Cv!RxEcB+w)?vCz3kbckrT(wQO1cF_v4=u!`ad# zF2H{jcFqTEJtN`}|%y0XiX9f6;= zGD^lo4wf=l#UuU~?dow7O4j}oGKM)1zYG~m1l>s7lc+?molo`1t3*5kI`auY#ZHMb zkLhT{&DGf!3Enap8MpEG(Y@cTUhynyTobF?^GzBBiuqJ-r`tcv$;)iBva`u}yq#w* z^4@(pZg>te90*2V8s_=%1NMl^Fwhf&Dk$2_qfUO(5kRU;c*)zc*KfUR2{+S+)`v7KZpx#bCyCeT^8w5 zR%?nqR6App72Kc$hPMm>IAqFuY8uQ0clZ)<&_mlUW+mn5QWQxldcBLoH37@4H;G)s z>0-z?`roWOd#gS@L7w@uuLWJERaK1q^Ktlq#!{%0_b5{u+w+nF_Rb)`yCw{tB;hN& z)l=NKo+K{?%rxzm5Fz(ZO-_h(5lpDl+up@f+HCdC3i%>cPA8&^{oN+Fv%cv;wPC9u z4`y^1{*|?FMtBwdK)DH5^b@o{i?bQHpKmX=d5=Bvi9^!XM_cj1sKXCe0@W|kJGu>` zReF4QBzO@Yv4|p68h8l*7Huk`Gi)t_l3=o_di=cC(c4Z=1)Z!|T$=!Zsl%0=H^=PxxhauUKEJAdAH*U(ysF$5-an`h^ zZgKKZs&>?#MnsMtC9py53lgvfR`{e~^3prFU8rM1*@tkh{%p~RAOxeopt!gZxR5mJ zIuw_yCqPFRXeoF}yD?rL6g>l;kVm}u$ZAc1f{qdxP4ohUpQH>rc^Q*SZQdC~lv?)v zNFCy)Qm8FBwB-!?$KF5*fG?ulueE+ZPHOOKy?*ZC16__oyVUSkb&QR%oTR5E^0Vb@ z-|YRt9dlph5ORoTYGQ;iASxWkD*aPuAZY`B<)@p_&$|=L-|y5D73yYKY3nxawO=;B zNMA`P7Z*nL5sSg8h-$dZmeWM3G-NS!ongFLaEBTtP(i=0H206T3~(UwDOA&x{Wb$!hz_3M&sVaQ8v7T(z$L+75w#i*|M}t4`zP(d zqCIy6-L-5Ehj!o1R{j-Wnwju*j^sJ0{Bkw43 zPH}7t-%d2=+~x*O$!r{}hlnbzGM?>C-kPm?Q`B&S46O%tN0)yp8u*UF0VuI?Dv{wS zNj#sahBxiLVqdm@PR6dC(N!*8cP1)q*L2*H9u%Wk;_m|NFla=7dB=|;dA#jR|5uN! zCjbC(Qg|GWeR6iU9p%kEPUi5Kf`zP;yCeBRUu( z9nQ|Y^Ekem#~c%?k&sy*2&Q2&Z~XLUH5SDW>|4teL$VWj zOaOaHT%lbTCfD(%x_pD?ySkMxN|#5JD&qpC&@#_-ppMJ(>n#-aGY~f<1WYqtxZ-v4 z4$%?LYWAmcYo8xfiW$~eR=>fABB^ap?M03Y($~ywuQG?Aiv*A=kAHrWuTuVjjS_4# zTr1_;O4dKe@NHp-2TStnvM-oMn|W2T3E0rH)<6BnP28oBlAVvbj)3K=e|N5dR!~p_ zhg^t3IoOxPKfse1*#8nl%*sS76%#@AGU&IZTDNXZ8X?T_^~Yv5ZE%);etTGlFCHu1 zE1DK{xb~%Oll#TQ_dNPovET2$1UplsNV?Zn+@ojUpdjENJ@dFa?Hw=HK)E|rc>VTm zJSGv-2eD;E0P@r(Z#bhzd_eBBS(uP^G?kY03MzeL*H!sTSZQlpzmnU1@SuI6= z|F~N#Ifz=M+G2+8oW2%T0&a1Fl1F0t_52!4MzNvRX0+~RQpgEmy~nwhi`92W?NR5W--<8} z2_1!OPggP13Hfe)YgYpTx38vSOmrs;c0p6wIH5y;X-?w%vLj18$Z{ zi}W)hG0{aa!lhilGs||ZPrgi7Ohbu6z+RHqd=ln%wp-=k(a0_y;TrmwhlZ7vbts1c ze)xj$z~_Z!-rUJkk$ZWgCz$FiOdZ|nzJ05@R7X4@6HvT7-C_O)x?=Ru)-}e{1 z?pVV`Z)*Xtq>)y!)V=6WY8lb6*Q~K%08ADwD`A3$^NlW-t2?JNR`JPJ&HQrgMIuAO zXDRMLz7njyjP;&!_S3_NqP=0Ein~19zfZ zMU`J)lQ}$Bd}=YR*nGKJ2)}yYxYi#Zzg5y88zy|J+UBKW-G*{F$hXjQ4}U&Rg!0Yj z`O{GEJb}Z%nor529e!KG&eT2r{qxdp)O)C%NYLO_%RNIN3gE^P(;qbe zlh8+><{lL+lUM%Yaqir0vH(@Nlfe4C%jgsV0Pn0JRCPK_SNC30vPhXsj345hF=_KH z6PTG4{@bs=$Aectl4wWdE99C6gD_dp%eWK^6ko@&X%&nxXC^!(P<_5n?T63uA*FUj z)3%rPa3x&S^rv^IL!OTe$7O(wQ+F8I3GF_$zr*qX_fW{ge(-@Gujk#36YEpCHy-Hb zEkF<0@wj~FU;q6;ujS@cW6`3e<@T~L#eVp8JvlX;tznz%;%G>;N{-7C{k-kfqA*uG z!l+i?ct93{KI!*ZI?HByr$y^j_TD)J?wund9w_S}!qF{4L3`;4uA%GOMy|U8TrvLHhe%03{?U(5`gP z;YTk3lGCkphsQIQ)KbCV+O8Y4S`?nnZ`#6fHL-M0yv<^(o__cN*U*+bYx6~OXra{p z!Dl8Wx-?JBq(xg&DfpfqOf#w)@&ofEG5U7K2#AYP**Q-AAo^mh=3M~B%3oO5V|CaZ zwRz%BK1=_Es#vw0idf4*3vF3+@jlReY&3~ko8efULC0>~xLjjPpkW8-nlU`PEPeaJHo=`(-e(K<)fvWc*z+#-$G5XrLnU zS)vvYrRP6yMTgewk|intF`rrTe@`F)*^h!=VM3`9lNvA67K&y0deS_gpWe!tAcR6X zI=gR^7-b8y;`tOF>>)1;ne|MCE*0PzpZh=H-<0`n zO)36HB9BG2+`dh`Z;nl?{&!6|H7TLb*K%)MrF))#CGS=TV%r<9A!uuY1?<k&xsjBd^L!RD9=G6YcUG;sSN0r z5;-g(1j-Th4qK)ZEXSmG{=YF)wnHA7cmXSirZ<$+0G?{i_ibSgNN(KJ^w0OENZc46 zz5=Erzk6M69M8$W#;Im4Y1LS!7C$d|@!(-eO}2FW`v;Ow^l&Y8+d%aaQaBUWD@ndx@B;KNoyyeFPxMRiqn9$6qO{k~R93>^qR-uA^ZlVC>$ zp4D!64G4b21mZ8Dm$2amxHW^%-phssr}w(&^WQXn_$pk{nJqfbmj2-DLe!>ZTzzL^ zJn4I)yf?>`i!(Mnx@WgqGt^+k>B7^xV|sUsyh;0c#_-}4d^kICT1=5Mu^QWWA*~FM zAiY$3-j0-rdkT3Wz=uM)!?!ib`;9`&^m+BRonc)1L-g+t>A*?(x66-NheDGt!n6KaJ7FY?Ar<;`ey}>42 z9=((jap8-25%D#@p3XOAAbsM~!<`6kl~mQ)_YLx*_JnY6|p!ZHJs!L=wY-uWxG(C)xM$>fZFd8^`l*?Wr(AsY6}lvi4HgeZ;q%)Q3axY zI0VSNT)4Zy7Ts^JF9i1SHyrWY!;oGA8z({dJ>if6R1YAGr~uLi5$Mna9H*Sjm%*6x zk$yhsc&f1nshdQF0UJWiBa|uIfw;5JK~WScBHeGmpMaeSQL6Lif8OuVN)L%ph1{K2 zweDaZ*h~Ie$^uA=jLv)Xo(a5*0Uw)Z{`ntHVHgrp1p{zdgMrInK_rQLM^OyG1Z$XV ztBbrz1@>Y6Tw4cdSW94ft)=a``UC6aqYw@&Va_bDKaK=ksH3{fk8$)Hcu= z2Z8ynqPwmfkfuvgcl3wI13+F491nK#Qib*B>ea1hTPJWS>eU|@T_QJ>_VWoNW=n*F=JYY#$Uzvm%;PfQ1 z!Jmm>V+9kk*zHeH{^cM7kyv3ONktHcD(NG9Th~Fb{Q15&~s* z2B{$wX#oN0k`57+k_JJfLAsGrK)R$v2|+|Uq)S2&mG18DhVzW^e((36|D5aMf`L8E z-p{kw-g~Wk-S_>JQ+!E&0smz-8Y9iK_gPe71Cz@2|E^Kuxrt8c=EK60nErl>O#|Sh zB&-kb-aHWc#KgSxc74iMq9M`ZJHWs)#XzLPj$L61TZH@HPg3B2&y7bxU zO6a&$WL11IX-BLab+dsCtjKrnqWcw-P`? zAn3OJR+|IgpfTXTp#im~W|`-e1*Sz~_fZ{f^(!GTvWz*ih$twN&in}H?-Bc1U?)YK z)q9^kAEY@SyMIGeRG;RY**RMTY4gHx?`t&p+2&l*sI3B~U?pP1szDZ2E zc53n~k0j6RV0B=0>Wgf|bz`|el_tM%NWEEH;R(iyaNA^Rc@im+mOkFYY9z(W5K5U7%m1sayUmz9fw40lL*tVcMl}2G0}4;hS64 zOSJAs6RuA{$;iE;S#`3vR{QnMePyI)7LiyDZ8BD*EBZTnUf#VANT*uNOi+Kb(pXA9 zX1x1+Im&vtNOHR#%0=Q-6`PQdVBv}?yyNURfz6G8M%uRj|E1Cn6!`B-+n-8rN}52F zX*p!-BG44t{^SNA>dq|oe%@1r5cv>d;368n14?;#We7d0<{roTubLbB|EjsgZU#%R z!#fJvgy)Z(J+r3-<4vJSmTsm2+?MfKwcv)YV{2j}mju*?N+?YA=;N)cLHp@Oh(Fs? zd;jB(ZyT)Cd{w5Bfo|E;Y54L(RC%D8{-h#iDpDI85gJKvX!8SIsM292DHQa@=RXq& zh|0S|c+9&gD1}`LlrzNiovw>{Re6D;b{BNTsfec>BdLx1%mz!@k$IDwMm%Ynz)~HBIOd ztv#l2NZgZP_#nqJF-Y3-QhYg|1x>t(SvhSWhdSz&H|g zCM)aPu@GLgVbufz_##|8+T?@&rmuhEr%xG?D;1o8%&yCGt1j2>1b{>PPTODvSbECe z#m=njIM)U~l7IO14Dxb+{pMb^{oa%Bp6{8B*r7#cjC=9r$m#J@DufDKW&!KZe&R6C z_0|9&1Pyrpte+4U0^Q-ZY~r`n{8SZOVzfakc$0CW96)G0N7t1ALbLATk2e}h$e8tP zAm6c1ML+>Ga~H~o3}WpXE;MP~rXk7vHgEP<`s7l&>Abcj>-;a*OM9hkO^Whb7q16k zNWtMZH^G65%;(#W9iDA9d>(7YpiHr~jw_P10=;Lig8?mneih;4pX-C4QtmwKm%pi; zgk1E|zOJ@Dcm&G&Wjq?#=nhv6&6pgbG4tK7?l zWqC3GH=ZEfZ^9eVTWrh^hkQoFfd*>>nSCbCBIw)?>dwzzg1Q)|^+y04cOEbiepR-5 zY_>$q1>kc2aJv4@nNY;s8n%;a!U;}LePeqEPhh$Cm`&x%0m07C3Y*7*Ano6O)3~I4 z-_4jBmOmn*+knd;TGQ#ld48cWyEQ52*wDPEng5VQ3;iBHhgYxrAB172S=s$P0U+`b z(xD3vM%^c;R%;GDQ0kyfnpnO?f#~;`Mk=oee;Rm&X@(0DUKKhlLe;gpo z&3&^dcv=ptErL#v-^q3qpRW_4oT%sc^8E5_%Olo`KlAAl03@Sd(+C5r7jnvkVBTI4 zUK0`J52pZSpiWa*2Q5q7TGHboL*MvVRUw5+8(7DaME#q-mnXTC2r5_jc@d)oU9xE- z^+Gc)S3mQG@A-}#GUx_>G~&kKsZ_+!ZLhfqfik~4+0r5SZy<}kS#8(q#rua-zxoZ+ zykB8vrx`Nvv?(kc2X&P0a`PLr(^vO!isB0Ns?~9$)!^1X;Nv)ix)M03cCYu@7A%VW zx@%I$DTVg|WR1E{O+O{^JnO%;GFj(|JjA^~Bkr!#aYejBEEYj$bnMU8Z_ZYS9xA0C zRWlpGu*N^z7nyJ%PEk0=0Tfdga9|v!ep2+KedH_n96bwKOeSbTGWmeRN@l(pO*myk z6dD;LgWEam{`gLOAqQ(m;W{;kln_4~^G2MK=A9rY)AeVQ)y#%C2rS&H&)VoX4vMdc z-BDPG0GYL|&*=)YAb9%d+nE$4JN5Ak`DqwiAMZ@lmy%yn@>?fWK=mBjNWVlrU5eK- z_&K6gVA{G!$zuI!Fs633C6YuBVMK!?mTdRqt$#(~TX9SR+Wy?YVe9waR+5I(vZlXu zrKX#nVA*O7s#n_TbG+N_3JAix@zo}fVf7dk_6w^YKM!*1e`x1qV}`bNQZ!9xCmQ^6 z@}u*z@{f9vZLvGwIhr5n2*^I2-0jrw^(UlAt&JKhUAHEA+_tBSrjW&{o}~#=5sb8J z4E=A5$dH`$wYp_bGr9cB(R;brEvEyV(K2_=_j;G|p$Sq2uG*4nsnoPgbJbYFdFRMY zShqPfCYoDDNO^yrM!UndyM2fQTA(yJ7fGvcV;2Bgf=0G|=m0@B9A6537QKyCB0xMW z7zUTW(_ia!|2O1P1t1ry=WC1jRMb_qjRUQ%Zg)poVfUS?(2Q$rdQ$~8gq&Bid_|1B z{lbXHmF*d>&A_ziN-eMP$y)!|VGMQ19L^(1AkgNT&M}WDZoIG=E&J)dP^=J0ip_v8 zZkx%DkcYhNamzYwq+mXC$3|39!IzdpaA{8W08_?_Ei$dx_?jv#bb|Yhpq~;&fkh^c zUUB0=-+l1`&U+L!1FYm;k(N{@fMPYHSj9)yGcI>su3$)}YDZ#!26lb+`Ya9AJ*;jm zL+?jM3a?t>z38)X%4kIs7DL}h75b0s_B5hLMKjSlMH|ZevJO35d4wvVt)rSnXb!(3 z)j$)s>PFJ|8H>c_f}Iv@c%n7%jbo}bC@NlFSHyMZ?iD*LKvqL9sZ0jCu?w7TzV7IE{wYJ=3Gh_druHqD41Hm1BEie~ z%X{;YgDU}`RW7diW$%Hy-(8EZ!t1~|!GT3$S~k#H*u{W;(gGweDt`v_ogjHW?!r2iF+JhY81=K%TI+?uam&-fqIAgK>t~bLfA0-dGYsFzo?X zq93nB!lbdI)ST7>Ro;EI+xnQQRZS0WG3?I}<%2|F$bA}Nm*sh2TX9Y5sG0pZmjLvD zli3;$?%aTNMA1vXR_wzbLbg5+>yQ#0Q+3(+-X#Xnm5D@&kztWJw z%*v$R@|a*vzkWdy`FloC^r9XjNE6n1@EJCMlQLx&>G#p${$D~Z(Fn99r&(XnJ+G3R znL;Sul23aTArMGqay^PB2M~IM1~D!#KnxTGYd0l1=;G1JJK)wQ*)f~q3i!@k+Pa2= zZTmZbn>gTrYybEK3ngU+1F7jkHDFYgWn&jA+?~NRtYXX@CPb6#TRK$u>v!6)_jO}y7MI3)HC5}+J zJxdg&!lqY2TnRPX9Iw`eC7sesO_N8KH2}$0&4E9cdTetWoJsRjYa$Z?sC`>11x;gly<+NDiwhL@KWtgaS5CCJ6m*5VeFXkS>n6Sq1>sUor zNW@V4aT6JT)sq<=ffSJfK_-jFFSlw7jN*oV>5KBYIh5kg(iC6*4E`O|50B7e8Y8q$jNsp{<@yF|4BR z&=lg6!{_c6{gkQ2m`n+vGy08VvEO&wd%m~WGdJw8wzyws_3o3IcEG z$T@ndeJi`1;$uApo4lrG!fQbs&Z59+;U85V!B*t&&?T1)ioM=ukg`7fVcEyn=99Do z66R+zCk?bXh^yXAEjd6Z8A#sIZkV17pumGYWuRdX2q5g}m`z2SWP9O8U;7Qt9OJ|J zcUbNl!D}@mZ(C%ZFMUs==aM!O8c&XL=&d?!%E$glF&L;-K%297To`O$!YF1>06a_! z*O$Kn<_?X(WV`@?Hfkw%aQ+|`Uh-&|@Fp(N17eIS{dhJl)dt@G|1v%Xl=0Kqx%Tb5 zZyqsnJq^`iF|4nx_%&Sal_bRS2fB#NMtjM}SC1_^c#rro9bDeTy_D|{?395hvK=Sv zg&eVB5mh~=52{MUG1>oLxR)~U=zB9Ve+crR>T`{1XPl^q>H4q#qH?5=SiEm`$*aGi zuYnkayc8A*^=Cq&d=vm7QNa}cgUL-lf)4iA8W1xTuTufrD=nkhGw_sfTRh2@!HGKO z>7)L~E%{?ksxDR?2xZDpeohpu0EvMuTgUuv+yjQRB7l=V^2Q$fJn&;mclWXUSqe2X zbW!z5Mt>dq_dO!g|5!XazQA4>WzF)y1cf>!xcWTn`NRD`v828Gmu5k<4TI=Ph;ZYj zFT-H+po5TY)C5gmj$FB?`dzZKTaT?$rRVTr!!YF;0Oi(8XXF1+IGLh$!SCumuWShH z?ayM=Kbhz>c|NIqxNVR=Kvg=wX<1X-&vDoWOnDKQMQ$wrKrA6}rXOY)pUtiPstqiA zgwOKmqvk>QZ=Actc0wrmc*r>UWQg|c#Y}uU1Bk!86(ftkbRT3j*kR1KDXULYu9^yY zT8l*h)>M5(q7W+;gu|R2W?wVfaOGz`@kJ%UjtklAt+FPkev?5q0<_+-m_Gg`#MwZL zy@q59^nx!*|B_=+IMbDE@!^DJ%YS4CxnOLmL~{p!CYR-YqxE#Ojm6sFCtbJWObNe- zAJim&_4U0cqbEW7Bv~Ft!_HMeBuwqKPyPk#UN!<4%IxfL!y#X^egM4IgySVf<`d#X zeJI+w2|^tc?18#*Hv7*W@&MaWjNf+rR*lQT_Y!pST%sF_PuHXXOa2a4mFdP<<^39q zjgh%eac{E~a~~C(P3>en<9?bMh$v$3;2>&W2S$ zuX+y@dKNj6H1z&)H>N!yCH6B??mKPd3%%l3acRP?ilEw?&x%!zDb%l7^%XxHHjPP6 zX0fZ?k?l|r0oWAid`rI;-n?6VH0gQ3=RQFBalj}y&2zI_ZqoZmU#G#xyWma4V^L7u z_lf@ijq{g}sRmzVciBW-&kr#!A9yHnov$&x@U+9V- zP#VS=z0U5VfnuSA((FhzWvT(BZ{UpT)$k@hJk;i4E%1awDnjeYkQ%wF@u*^xc$@8W zt$J4+3nL1c=>`csXZ^7WrHES*iYw#TPUG4^AbyT;G(G)4B?#;NlsDG&Do1QWpiCkg zMv&bf(gd9VnZLot#zv;mDg>^JT|$T{eIr#ugj-`y9l-j4)}RSa7|??FtSvRok3slk z2uBJM6G!8r-pSymrNI(l$BVkSdWq9#IOH_vevMr_qgJ13f+!P+LLukN zZ}EklznT=TS4cyofN{#f*WbIkiNeh`6rbVv2=6-9;ApXp7bB%=)d6Ob-FGm`0!UuB zcCLd!6F})k@-;uP0(nQ=eX$cJ26Sz?--3bQgLYUk>4&*5YlX3djIzr@>w_y8Ut(Rt z(UL_yD=Ox@Qv~kg=8xGm=-bwxEJ_HFap*GrhhB0{5G!0D5h-^1Ar26u?I*TpTHu-) z%;|1Yn_t#APr&45Ykf8tjmLiS7wN(pZ6R3xi*rTse!t5&6h%M!59oS*U((w?C~Q7} z&-ClHiSNV3rUw)S@C&^!&+}?Q^qlric^{>1o&kK7!P3&w@o>cA8=$IW8`OFDj}?$1 zw8b#+u4m~pD;v^Fh@UPq7JtJaz5nKpLd`}~Ot6FKgQFVUz>ZbwmCX1|0!^0XZi-eQJu5#LULXy4z_ zdp9kC!&zsGqi0b^l=Qm4cAKck6vb%aU8_w53KD!2CJbmy_wA|xaOhn(g75l;7Z8Ll z1Pf~bp#)ve!1uzr8gNmZX+Ax!9Q3VxoJ&&`U06JZ55sCD^ua<&Ho%=f2AlZialv=&YBk+HTFaRu`0@VSxCOCNq*x4sIjov8WRecm_ zwO5f=ZWoyHdM_@W4EO_+^QPA-#~s5OM*|~-a9~PR6o~U8>1oLy;{XNp7o+#Q_SF>y z9EnMLmD!AGstPWUaFlHeu7=nho}cWYr$9fVfuyz0D8OCDf2;{ir)>(}SFFswdT-Ba zAU1No&9d5>_rY1uMKI{i6##QI_5x}sv?Y*6-p%?5lUCaamm!&gQKyl6a&1*iqj6lGHn) z;vuC{emG-S_H^DDh-;s-4T0+U7^VF@0se2f=^pv7m#S9tKV|_C74kzqR=dWko{~>&leV#wdcZNE@Mrydw?(VLzA|(=51pGMeg3PUOhNr#x*=Pqj!3iP#Om* z^tS$x#zU*mb^JqhiTWs_SUE#NM{anHg&r85wiv)81;(i9poKs zLk34`YrML2F~U;?#lLvIHK~79=P>ux5#U-+m9DWfd^HJ*5NstWkeK$Ly@|m@LRE&F zDB2z2ezpTBVfnF?+U-vgHMUzNI*df*Lw}T7^hWKOSK11%F(hnM>u=R&z-obB1yN-< zQ!SaNQ<_sz|Ai$KqEl)fk-}=?!c|b!p(c+j-yJ1e@O1hlJ4?^wwb(S9I+MzU=BSfz znDZ8jh@hQ?K3Y=g3UYT;SIbmy*Z04eic3k{DoU$2eSgx$_IdfO zycHRl^}`t{^K#p1T#OXoa6;#~4C9vI&f}fAg;#8aKPF_nx$5>~3VYv>E=)JQI4vW1 z2OHAH9Ss?aQ&4&tTZUr0)#&ReO?+`K|M8MZVp|6Y3g+ACeESRKZWXySsz4G*&yC*u zU_}6&Urg(wXw#^>DB!nyLvho2)55GjChZT-r-LYofsMP=RGK}`0UT{6)ERPA0#I^+ zp|IxEej8W))_hTMujJxLCIknhl*O>xG27=eCJuh_x_Ld$5GQ`vcFKGZlmOD|4^nbp ze*3P;ibLZk3$oA`&>g!UvbZRf1KCkI&kmP^8MZ=vaaPw|Yvpn~&Ye0m?3)o09I7M5 z5BL%Y^^F7v?)w0PKJ*5tFJihJZb+JVGX~2rs{zeM{ z)u@c*;%5-cD|8g8!z~OjN!VI5ARLy}km9VqcuZ{A3WN?w>X7F_kT8Q&^s`_7TSK> zKg|UmO2Z%hbpYtpHiRP_MixkHC+X8q_gCIeT&fwBx1<{N2<44!qV9#NgWQ0F-vDrw zPEph6O^%fTn3Y^C1FQ)HA(;lO$9RUnd!NO2@IMn#Tb4e4cw+&E<<}^KNmW+uw6uzs z&hnLxmQ!K)Lx$Vz%Ze!-!a`MzBk**7-dI+SOmDQrwr$^(B`#DdT2@ZI%l(6CJkKZ^ ziwv1L$rf-IH~&YPn%@|QyLT~f`q z?W1dA5o?)@RxQ5^GaD&+BBTm!D?}@9!C;Or@l5Lu6T*ER7e$D)AF z{EmHl*PrCPLbUPsJ*wrf^<-Mg5P%4)!Mi`(ZW5*Fw(?K|l-l32F^9bVXn4HYdl~TX z1~t*G9CR(poUAKpgsD`{-QKXPXdba>6Gp9cy+&)gt15P4W|JfwYpy_N`nWR9nK1w2=~yW*nSnIa z=LX_4@;S9KeR2>v&Wj(!0=UELvqxQFaL!RxP&9_$h+k|CTVs7AcB1QNWE!gB5Q_+SgRKdzN;Qt9RrHhi@pjAYgo($cNge&W z%Lrh`k_(lRk7xc68!s`!lRR7IN_scEKvK(Zv>=&p);iLt3%_zKx_8}-F?eIp#zMcf zr0(gE;1(l991*H@-C#`#FSf_u(v4UEJ+Ev;<@Y5|RQYOdDh8Nq@Eg;C23{dM<8Z$* zZx{u<^*W3^OUoN*U7e$N)iW>>pRR_RzX6kA@&4uEjBRj4X=fFbFn|I#D-$?$sS)ru z5AV;#wZ?%2d!?*cz9%ez&MH(yTYLHo%nG{|e-@9S4zslj+gsY(jSg!Rr=qGc08Y+k z+9N@SP>bY#L$n?c)aQ-G5PBQd{oZ!F=o`iXLmIbU12X)z;39<$i*y07LPs&Co{r(? zPCNToQNeyOLIPOs#-)ILjIbo3Ua+UOR%ifj=e3|m{ z;plo!m$v#7>X(n(cI-LJw= zJN}IlV5-xjaOuSLGE15ocl{;Y%)*+Si#dx#j7ss@NXJXo>)Qk#iobjELIoChEfDWs zbU8R7<8)PK$P>u;qR|tnbp`vE6`N|N_kr7{0cJrYj7Sn0M@5$YK;%lb)VS$-#1vEP zF9E`f$o^pBBewn)3WO_ff?~?VpX+g=YkzQpg(F*-czk!PHjU5}*Y1 zze{*q6H#UV=7=_vMO$h+Eo!JH4Jl;gu>WcS-CM4fk>}c|`&CGjBB1>HXa6!6c=R=f zbls=VnF;E?4G)u3r7vw0Ix9d*#bx@BoNbL6c}IP4Pxg*AS@ zlR@Xq3>AHGKr%&RQ0$Q;Hn&h>AL+~>X3qs~VCCumdv1V>?i+%`;6J4^BP=S93lKLL z>l_9n0ObJt@@0;i-#zIK-=i#WLTd|s7pn(=3 zy;hU_RZ+}PQGJ8scLY-X6dL?LzD(z^AFYV z_oTRtAo_IN2oy4#=KKjMF0P`2r?$B%6WXtXpnxf&=tmZ%Ej|GP=QpG6309J|cE`3ldbM452kC_>@Uso@AO7BX8?NHg*QjZ~ z?Bb?e`L&hW^`Hm!q4|UalR!k)q+p!*4_DCv$iR4Xq)Q*I-Hs%#JclbW)#9f9h#;!> z`XCGtAAT57q7x-9bSeJfipe*op#Gyr=?;{M5G4{4gTnMC07C*1i5=yw_@Lx3*9i@q zDM8|k<3-b%w8p`Rjsu@535zo=G)KUH2Q^|JIbq_`Nv7@^a>q0b2S+9>f z;YvpX#^Xbq>(aYZ?kSy2>4(tjmrFDl??Dx^f1zVs3zfvP{8l_AQ{*Y8W8#R zmLyZ?t$@$~$OB^q3??jwx9@J9!mt^Tf)*}EQ-&KW4(7?p8|jOOE$iaZ8NnD zrMbvPE9N)(skg4XmVDxcE^Pf%M*b6zPHxTTDQBK{dN0>2Qgo`uj6)tBW^M|Y%9WiW zB0T{6OES&ok3#ZpIvUyD>C2_u`xyyYVoCq-*_sh2AVhgAJa zZfC1z9>y7 z{tm<&;=4PqAZS1BM@0Kl4Jn}*cZ(j5LlUYDv}oE?mI53u8)a|?vy1P|18~wEQB2bVq6FT-D@j42Af*2*pqr~)eidV`4}Lk#(4~SyKs(vI+#DN z>8ZDI4dbvaZrMCR^qvAH3_Za|g&i00PJs&t(TDdUC+ipKU`cnbwB*l3kL_?Qc-x7A1| zU0vM{{^`?+YNya@C#>Q$h-#-M73C$g9W^@%{pUZgk5c=b1VR< z3J4V8)r_jQ+_;~E^b7Z1+)vO}iF>%cHeNRD{CkQ=yPpqEU~@8RVGmRF0OndCG0st6M!wfW+7pRz^Q|RjPA<~1ijnHEXjSW z=C8K@=0wEVo4Q8n0D*0Q zTXwo<$fz4KwOE$X;6%wU-y9SLi_h^HtbeM(P8Idk42oTa&obr~eN?-Lcnt?yMd*F; ziZwp2D`w`-=xa`i z1a=(;lr9YPpk?bKJlBicvw#GzKF9asK+An4JJAe4qg@9CuEHV`D8X4=9Lbb3F|%qM zfNk|K2x!^wi|hn?0lwjGBRW9`!0iV3s03`|{LwLSMqB85QSfN+HAzRQ>h@Wpux7eH=H2|b9|Eh@e!fvD_8>O+z$770| z1YRjTz_f)lCYSueXLz{A)ei7!(>&NhGXQI=$uILxGXwQ~MpL5Kokhkiuo<1AriMn_ z@D+fl4t(CY-nYI-Vdsx|jRFY-F7ZSsp}ryZ=_2kWcB;w7`+xuvlGfrmq-^CRTKg3B|#zvvaU)m{J6Mpij&Y-298wi!r< z1OS_Q*d{>6^5K=ymBKSHd8Pvyo>bW}e>C@tgP{RB$-tOLdFnFez3HFUgQ6rR;b4`h z$98p(I6f2~x4oklysa& zh&YQ5>d!VchOR#$e4QK3jol8L+XDb@=V-YVdX6^W20oO^+iWT|ResSk12}{-5ga0S zrt$oXK#xh1f}u53M^>=MxMnnL{;b1OUcxZ$3>Tvo*~F=?iL*c9w?d)974|qxM&IR1_AOvL$5Kl zLhWLymqcacoY>%1|_w@kwO?bbMT zMQNRUePnG2>^UT0o2^@pWFF(@jT<@vFB0j&HJO+#wEl(ignc+WiWX558eC|h{EdVq zR`+3%zke(azwB`RQq+DjT~*!lDzV=oI+Jx`R?Y?Pf<_{TmLXb3GtEILwc&?0?)rW0 z)G0tXT-w1#-4FJ^FgCq8-a2LVU<^{@M(P$SPw;a}9PT~dP8SO+C(ey`L z0gB_>$?Zz3Ayo$nZxl^Azt>kT4shA>1iTS9s=hY7y#OTUQl5!U7W4k{SqY2rqhUk+E>h2_hfnORSR0c{|HJePfSVd_ z)}Psust3!2JRr+-TTQ#obJGP2@zM19Iz2QxOfdX*{g`bH$Fu}*sj0ac{@6hJ04BrM z(M^c=gL=Tdb}q!kF)*Q=IAJB>tG(Sk#l%c<4+}{Ey05429hOKtf4vR3wN|J?8- zn4w&&A|-I=y`kM8eIwJfI9&YMQ*wBXPdUK6FC3aQo3=3|5h^MCDjZ!0pF$tdNkvSP zZgXuj_~Bkktb|yd_`bHGrq{J+y<=K_a3n9RgdeM!8cyIGwJk!l66YarbgHp2C zO)R=TCsq3^zzU}lci>7;F&mVaA zU1JTF&#~pGdR9s>H9ds z2F<8$q47Gx#d)z?hof@kt+iOHl7a5C@w3dK`QOj~2`46lgKAYjG^R;M)8c&~q|FbC z_2p)haLgyjpc8bwR}b(=KNrMDy9%?d$_Ehj@@J3Yd$)m@>g>(EyQCsSBXId5CmH(y zu6y4AW|}+c7Q*Vr8(KNrvEa6vmkZ^Tu2%qENo8Cx2Wz;OMi&B%5=eFA_w%5^R6(bQ zp;Sqdt(3-uh2qEE%SnMmNPtce2CiNJwjd-m8tZkd=M^=A&GbzlwamGYh>g6M+$Fx_ zv#4pi_y;bKcSkMHJOxKudKHiqkYFRYdL~-&lIO21MS4pY9b1L2Zk)9+Ex{Bv1v2aI z+Y2WhlUl#;KC^s@>(&C9a4Zw&7j4T75XzcOWJV^yyCAjA*irJUVP?0(D@^bCO%pIT z=NY)o1o>^V1TGlmvN1wo{77YNXXD&YMh1upRVJ4f+}$>E!((N+VW`y8sBYTt=mA9> z`@Clmec7>!7IbLzQbr>IV-y?Ij!;Azp{;plYOXBB?GDnCMo8C~4+F)1-ai}Jdkb&xq*>h%+o0-DKB*T~> z;ZKtQNe5^pDgei?hsT7Dh@v?xm(?bQm6C6q8g@~HV)KT7hr~*0(;kW@d4_;*t0KuU z?Ak$s#5P<=D*T0l6e%C|RkNZa#h)A|Lip^(Hq$ws3C03&rg}uLk028D*jE#-MLwXc zCd0da2a8RjXn}K;4p8bxAK`|17u_DMpn<|{q}ly(Ye|fP!BtUNU;b&}GKn%)O&JK9 z3mmzS_x6ihDUfB?8Uz?5!bhXM@flSq#)KG|vC{ckn@q=>R^CU7sZR;Kccw~iajE$8 z+?FV-m^l#dzdadOjb~RY=eGfR=f}kIPay$qctoKWu9QtC*us4D_aWGABT9cB6w1u+ zHz;&!Gra@f(DNHEp%Dg{*;qSRJ@k7^%g$NcA;VB(P*g+G95{VLqKWPqmWZ2nl+nw| zJ#8dou=uzjvWncUoL;wRSsdtKM(Y%}N~0?_5J)VofFa0I{&WE`NfA2pRRns$etUNk z5JPx6)W6z%AlX1)RBG2k#ST-V6K8ZW%V^pDVx(kYjb1=rD=OoQ$g#+=Wpo<;e#mel zwo41H*I{=Ay+|DbsmdY}`h$hB$p;kRe&94Bys>to+FzD#yF4t^)1j#_%O2J@{7^qU zf?wg;I&$rnn-vXbI4d<3yNg33b&j8VnOxDaaPZ4C0a|@Nj7@&sBa^?neml@y=*1vpGr3iketwz{c-4l0sWATJGU) zAQ7uJwKUG9D4RrAL}M8G`r8j%W}!9{4vill5JGn#pA8Q6D&2lIg$br!880dm<3zbd zkd57dY@uig-?cp_)o~EL_aRWEe-Z3C7nan$oeYC%?50I3!L%*dPssgr4H3F!U%(A} zG@KK?C;r@%MdDZnpjn~(fSdA^*!6Dxq84rN>>G#YuJq6#G?Faz>!sXdRhK467_>aPi?hF2+GUVo;5rwX!-?p2+NW>721XQNg}HoyydyzR^)WvKD!xmLvK)5FD}yPX4v&WPzT}(#i_U zMBPIn1c%CNOSVeU>YbF&m`l7)V#@ZaOZYjBcKH4G_nko5s6#qheIQRuSX%i~nFS;5+wCHKZ@Cvrnjg_CRZd%cJ<)2g*;Q4Tb&dsq5&lc};d!ZZ#LXtPit9 zb_>&F;I|e(YR8HC@=2(j5I5!T+Gr3vB5g6(>+sOF%n&pb^{h?Tt%e-y98eNSkdhce ziHom#x-FquIXR2ZU*`2XKmmxvV#cP@V2p-FZi>z-mEpiJX2UKQFOG@+ec^ZO)Ex5m-QxgC6 z-eeu8#cx{QwBUM^`71WR_A4HHH>8NVW_ndy-^dj51Ljf(l5f$9zyDz4LpgXct?Plr z;8b)_pLYEhch;vd*Y%h)+EqsIQ(2pK{VuqQ1fi$At>gNch8||V-_Q5I+5K9q5AhlR z&#}j3zNdw8HC1I>t}X4#aRQS*Q`Y2e-Xy313sHoaq-AP7dyAUHb3K9^cNj>KQ$9!U z3-pg-vq)H(dZ>mHag|84uGJfqYYK^M`hXiOvw5TURN7ukq=dr+KLfshw%-(A9L;8MILu?$2 zzB<3%g@$Z{kj!d-y=GzlBWeAi(kY~%{dUx8^{*QX^O>A!^TAf1<4klXF!5;;p0H_s zs~XCM_dotJFY_zcLZJF`m95{ISv5Uo0=V1-U)jVwm}AZu!fYqVQW8pH)8eeo)n`lo z9JE0r0sw+Rc`JMfO5rtR7GiX~n`S5EG;$?QLO4qG2M?%@rE zUQ+rb>ibc6i;0ZZm5#ej4CGH*1V_ZFZah}v-4KjRj%N9$^(i`&k-4LIu66UPPx;ET&6sN{aYoU}F{$h) zyEpRIDr~0cdxXZha(h!BT*)dQB`sy(QQ(A9gp!0}#igV;M*%r*{90lD4Grd6CO9sM zT7$Ubev+QrU73qo&t5UhvFea7_)jh|HL|+0#dW~z`|)q z$s#A~d#kRzlWsD$Rf{+Wd-0WJYS3F}TULXXWY0O*jX(dC$8slrPBL~qr42e>K!3L zkMlreurN^YzKpau-H%hH(f!!#DJptzk@H-A_bKRLF^iA+SVV8}B+>r1nF9n6&u5C&64@QuM&LBDRbbVjQEY}^W z3o(XUoK-KOt*1zJza$HJh)KE*!xVS9baBx{E}FxSSv=L=il`c#bjL=b7`zHr;=R-v03_L%tgiC zizB7NIu@_NCeG0Jijmpr#61rCVSQF@t@x8vlD@Ab?}{T2b0{ZyuzqoKT_Xvc-w?M~ zBc_Y&8Wg{Jx{mPfo2o;V#3 zFE2VQ^im2&z0D)-tGbe|g@Z8V1<)}?uDzDKj$ik>u7djR5;uHjc;$JuMb~0z9dpig zr{T7JKNub8LfyUpkntVkFQ(_UpTs;l^~2%^n?Js$9tdo~NandfH+*+XnNF~(q3mZZ zMP`?oib(DD?!AKqDu;<%mSJSy<}NVLY=6$x?clP*@(na~CaHx*7Y6CvunOz8-0Df7XZ+eZeOZ_Fo-E8) z8P{QDxUTYicWmo{WlH?0G;$Pbwo_tf*49iAuO{O!S8%yOlP$ewzJwj+;ruMcL+sd7 z;!W8W16Y*#kxi|TW6>Q;`>@k$$u(70vpaEU{-O1UU2j%(gN3M`y;<@?NUSY zLcDG~KE=Ist1zYZHARZLalWAkU`7;CGqUp(Q1zMijm)KPb$g0``{*x%+xmcUJWq0h z>Pi0($m-D-v>jioYL^L*Y9-IPPvn6bdg}EBr_8X9Hr;?zOpUJ}eSNRydU>qA2fU3V ztnpq{)|4l3lt4PNY84>M^R=Q^AC;OYM-Ph_Jx$m)en|%j+*0vbfG^PZeWThE)Au2dCkArb+@Ua9E#6*CU&uRLyfSX% zR-=v7XbH&f_pG1wfd^KrQ=4NV#wX`Tk8aW9pbR}Txm57U!RUvZzFRp1EP~Df4Aqwy zSkV=INIhJYmoZwDb1I`Tm;I=Ai|I|Jbxizz9xm|8+x@_F*iY03GbZ%LtVb$oda-Tj z@;!RF?%|In_973A&o;>iN|3l73S9Z`oG7huG?2~SdYS7`^ zx1XGq`W|Yxzp`u4!2ZF$!~Emj18>T+S)`#Ht&ihdcHLU`OBz!G6IG-qJ{AmXw_28f z@@+Z+!4b`^5Wde=w;=&|H^ar~jFKoJJdPcGzj9|oT}cI)*&JcmHt zXmkYM=G!0o@XxjtP8uTA=ReSB$M|Cuen=V4<0swLHf{i?dC)oG)NL-*Z@S9OTH(TNINGhtYTe7jza&gL*I zm*MMTMHyu^b$P4!-_II(^Ns6->Nng`r-I??=7kfmq@^LLdw>a--*(7Bd)ap$`CQU( zGZ;9ZCZQ8x;lqX9SIb6UvOa8bUMlTW<#x!HA5Az8p|W{!g;G)1?>rCXKIrep`WyK!e$IPU3c| z?xf~72+QM~xMxnSRYMEqdA#oi#n}E-CPk{Cm=co4L9AV?Q%6T#T3U-9{p+ogrcUOm z8Ik^J1(Qja>9yypaeK9N57w^z{kPCiAZoZb47CMq%zw$W+mk}M0aZ8QX9FMdgC3Ih zxpWhaoE*{W?6ur~)=u@?=LQK`0!u9yi^+l}F{MW~2Fh>YbGP%T!rz8BJt#Z1GpPSR zwvr$pq@sgwfomoCsB@ynpKXc%S2dEvEI|PC(i@=*#Fkq z_E2C8izu>t%qwHd!WF}0k#?J8{e?7E>VSD`iMWjUXYs`-+O~QsK#;chPWEriys%n! z2@&3oJ8>{&zb+$|Ewr|-TI9p6a&180@n8J zFaPCt0gDDg#5Kxq3~Z|M4z#5EyR+74aD3D=rItkYU!L2Kb}XxKM4u=8^I5< z@R*Faz$Q=k68^t)i-e$7TuQ|Udmh0aW&~6LL}z^1^Z%Sac_gS+v6#Vt{KWmG6spad zX=GZYI?J8fE4ovQNWm%_j-UeVdxBc*7L;K%=(`fwJR;tH>(GCr|8o+6IhE7jSN}F3&B1vw34VhI{M`N?((e~w)o}d({vEt- z4F{3btyW6^KZo@H4D3QKfpgG;3MSDw0~pwyl38Xhh`0i~knxZuEbgE#raVv&{Abn? WzmUk&7&4Cm2s~Z=T-G@yGywqJ7GqEV literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_001.png b/docs/screenshots/venus-os_001.png new file mode 100644 index 0000000000000000000000000000000000000000..b7a6f6bb2d0f7e80fa6a4ea353c859315386521a GIT binary patch literal 46750 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 l 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 P1|aZs^>bP0l+XkKQ$wOc literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_003.png b/docs/screenshots/venus-os_003.png new file mode 100644 index 0000000000000000000000000000000000000000..5d303adcbf344e7dd4e7a25d65d5e12cda9c04c9 GIT binary patch literal 38508 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 YIZcaSKDFC7jR6QeUHx3vIVCg!0NUTPO#lD@ literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_004.png b/docs/screenshots/venus-os_004.png new file mode 100644 index 0000000000000000000000000000000000000000..d129b80807e67e99516ee0a4fa4b49a91f8c1e51 GIT binary patch literal 29131 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 P0e@uRiVuq<9{c_eY$v*} literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_005.png b/docs/screenshots/venus-os_005.png new file mode 100644 index 0000000000000000000000000000000000000000..f101f0d7dc0b84153b29e98e5fdfec57e2ad0397 GIT binary patch literal 33257 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 P47@yCe0Eei1ttFh&k#uN literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_007.png b/docs/screenshots/venus-os_007.png new file mode 100644 index 0000000000000000000000000000000000000000..a8b992a16f78a4c96844318fd13216069068035c GIT binary patch literal 51240 zcmeEu$m9k{0EY?tviv*4`BK~6uk^lW+Mv#Skp!&bZ3{z!_gEsTT z`E&j=)8F#~12F&p0JvI2{-0`z7!Ueg{k!ZH`nq{`B)94-g8vvc#^nh;6FT4sF`KBn zQ<+($qeXF5MM*-~#5mCHU)Iv~j^<;n4HJ-my}?g4_#-Y1d23+b4E zU`q_QN`X#&1>_Y4!GGPR36(+R=Na6~3=m5M^7E?l$tdEhLi_Vl?7r_C934%(_~V2k z1yUlRvIQ9?WSP|17=C-(nlb6ct0b>S>A=<2zi=7V=rri7%l_xE2|kDk`3d*D6F6W5 z2kd;kk_Z7eo5c|Y(mc)}NJ=KV>|h|nDg{JN6tOV_k ztRgrJE`SX9H~8;R@cmN70ILlvgu48@C73Yq>)K-KP6&bDOW}f={=VqDB@=9m9wK`jS8Rg^OwI$lCjVt4rhg@i}kjXghUU z$wDfSbGdU+6J8+73aD7l@1Lr267ymIc{VeL^056ky?CgsOft*fH)>@LxD7cr6+Dao zs286Juy`1OPjel03?W<{;g%Dk;6Ij`mH>9+ax^{x_<@laAj|LX|BH)$Q^50~n8_Nz zSAL)2pV2-)*ZlXOJjMYW2pt%hSq5Kq3f21NUnv>Ifq5BIA!KpXF{bcU%h7)B{~Ibn zkKt-DPOOR!6X}x#p6p8(-fRA27iDH(0ORwiv#c-?1Hd1;p6!_7sHj^N=`!3Y=x!imb2wE;@4}8SB@PQu?;n68Vcq+EGe?n73S*) z#1AkY;adhKjiLz1BmsH{Bf85_?SgDSl!+-ki8*%h{&Pb+m#$4EbZ@dlB78ks_#tfk z8^dJec+B�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 z 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_008.png b/docs/screenshots/venus-os_008.png new file mode 100644 index 0000000000000000000000000000000000000000..137bf6dddfd72a6c3a9636d61fe245852619cf1a GIT binary patch literal 34903 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_009.png b/docs/screenshots/venus-os_009.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d41bf0aa55ffbfbeb5e105fab6146bdf29257b GIT binary patch literal 23828 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

    D!&n`4fX#a(_@#{`Gim%EoC3jQ$$5 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_002.png b/docs/screenshots/vrm-portal_002.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8257eb0578641825bae914348259c222a47445 GIT binary patch literal 21325 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 V9_#xUD3b}gU~2hG(HWP!{{;(fRQUh^ literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_003.png b/docs/screenshots/vrm-portal_003.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7fe8b0622ae8888508a4b64aab63c515e852e0 GIT binary patch literal 24383 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 L_@(maW9R<^{QMI1 literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_004.png b/docs/screenshots/vrm-portal_004.png new file mode 100644 index 0000000000000000000000000000000000000000..dcb43b47e60a1d58f77906265b4096db1d26e550 GIT binary patch literal 48559 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νP&pVpu%LDA*3YjI~l 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_005.png b/docs/screenshots/vrm-portal_005.png new file mode 100644 index 0000000000000000000000000000000000000000..82b87f96dec7f3a85d00f63bad4dddcf40e06291 GIT binary patch literal 60980 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□nb%S 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 D<-YXE literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_006.png b/docs/screenshots/vrm-portal_006.png new file mode 100644 index 0000000000000000000000000000000000000000..27c4760481a8ade4914d2c6f17df380ad59a706a GIT binary patch literal 44756 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%0D6l1<9wjawprEKDaQ55W#Ia0PkK@appn9LbDG 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%0DLqig1y#9ZfeLSijEeP){vDMRi^ z6XXWBXec3H=TaZEZD7(3(>`j596I`36C3{I>A)l|ozG9xD#Yad&A}Q>z0GQ*AKnkr zc85`X!op=oY;$Zvyyn>%3ni6U-{T9PPbJE zmp&g{mrUM{Tn2XGWNKa0`$T2eP5P?Sd^V6K1Hf)0*BPrLjWK!19BuND1utaQZ9{TZ z=(c#Et9ln9J(JA0Z{$WY06H513rH&kgh?qs@vxqw&evIl;^Qt%u!`x~T&cqQy;+wK(Yk~r4o64b?>fZF9EZ#kHy6Fq|M6gtpG6@6ak{reiZDFq?!b*A znyWSk8`JuGPjdlrxHm`(8=x`HI8%&9#sZ5eFV* z7f&?cdBSo%xgWFEcjDLP4^n8bmV;jC;ec#R&&h82+2)MbJuR`o;e0gFcLOB5L?2oQ zW@vzp(Zhjf!6}n@_-YO3v}H3>s!kL6!Zty-UGlv_hEjL9z9h)Pq^JqRBy?4@U$)!? zOWbL^-(iva1Ed{JleL+5bY)|l@3vZj$IGuhSsP2<_skEB&3 z=UipVR#(^dx(1BkPAftVhC-37)VwOWa{I7{@eL$cURedcbMe#DE0CN=MB>qTroP!j zL~Q*Oo;P$nUe7!7T0M{n3Hm%DfaBTiwPhS5eOMVHl*NX#U5m9-T@b)PlCaRNpSz)# z3yx2=^#U;Ip>2>?hMb>@=hJOVF?3$Qd5g3iHN*~1@%$dk}4Pu^M(!U1I2^b5C0qD4bzj-O^58ByBA+PL_oXetQm(rq>6=X)hGD*od z$ln?FXLCWbZYHghOtyJW@4vi>PK=g&M=w0fpQOaH(Iv#UA*rN`!`oCU5KHuW=Eaob zTw~!Y5J`7;5@9mrRgCxD%%?6h@vaZO$49o1LBmLa{7(u|GPkDz!is(&zCDfO{wooDPpomhVVe=j^hk5E2Vzp-E@e ze(ttJd73sr5^qA%iBjQ2G(t~00VKHZRw5+M<@O~XYyR@F)uNWHc>AYZxGG*sIIj1o zXyAcu$bCyid+SNsXY)%_7<=uYWq{dsQv{+yuLIsDPDl4FVhCHfg--AZX?G1(ZX|rh zDSB@@ia|9&LfM#ziDxYSU$-*08v$jbun=Kss z$)vKARe@R1beL3!L?a1sK@1Mm{MzXd3xJwsl72~wCW=i;QmHz}OR=qX(Ym=i$E{%< zIB>h31xQ)mG*?1M7+Y-#V@t3{e9pUtb;us^;iEv=OwweXVU^JC#EtjV84!O_^Z>k8fq^*4@6y zGh*6TAc6?XW-mUU5Q38UwgYTAOc<=7FYWm4bU*4~#Sc#_7dJ-(!&0gtf1>Oy9$BoJ? z_@z`3XZ`J>Hk{vuRLwDys1TN+mORVh7E4}x>(gZ zo@IlAqi8nipxuwK?f)O4ziAA>0j6SLg`#uO_kTG3|PMj5z?fQv(RKlqPAv)gX z#lAxihMwkZ2s_h5*u?Te?AkWnp8FL>~hw=0i;!r?2%?%RxDo1C&4A`eUjEW zzVB=G9Ia2)p=iU~FI47~rKZ-msCpcnEl1VsqoahnBP5W#$=}y6HB!j4+vSdZMaS)m zS|m?v;4{&P7X+Pa(*VHX}i}7?(v|7B_`X7T(&^5Ru&OS$Etd4_Emk#{ z1x4&=YdP#vs|;V~ZAUBab4uV(jG`o>uBO3pLD?!wMSM&neR<^0>~de;5ppO2Yl{u@ zTfGZq;|4TjW`*k5?`|W)9x@!=ufb${zDB+heM_y^ZVJOK!Mbu1sjzVE8Gu}bzmQAB z(1BYc2p?_!jyZ3fZkkCcS)mRxaeFk$Z}|v^cbJz^-76;vL9j90#);ol6b!QdX&PaeVD3vr*O zkwp2cgqK5Y<~>&m;<=U{jYxuAs$*1xtHkSpXm2&6`Tuw-B+vdx08#yjhs+4?wjdShE~98ze-0{e;)o0)(A1hh&J_p&q0 zj>3P7U;DsSjrAj7t~VXu|6#6=d;V#z?BO2}-q}c1Pf`-ohoh>U3a5d(cD`l}l%tg_ z&cr=)#&sJSv%0uSDT&M+U)kY-R1@Rh2E|6Top95{5@Y_8`ohi5K= zG3Htr#P2ql#;uQK2gpR|QqJKbk0WoIiJMt1n_#3{2X?B$asZM~_PJtAcg z10ew$eRnTbw^YH?Tls?!DnE5rdaiye>UmsW<@iWMP(%sUK@aI(K4Nz@Q zxEfdYe^(Fmj_g>Lsgn)^Qqm9PH*(-YmlTKA+akK(Ut zlck#6T!zajv_ep}3ctp9fZZvk_t*@ZEac8jfX{?uhu&3l#-;Vp3zlDw&@VabT8n%u zoy|_{-0qGtjCusx7>9CeG&dO;FQ4hwPxI;vfPIU_Wj(@ke|;_Id`i})nbcdx9AAQQ zMa{TQ8sMq4F-l|pw=KYoY62)QR<=eYYUFF)k4=ag6XnmV=E8KISGv6qet39|&dfGX z>|!ecAFFt1oNM-jRqU=ChY{_+E@97forD` z!8|Vu7td^puLtZy%=i6gk>AcRx3ciR9#(}+Jb$s|->2^GpWVV-ij?=c2}@Zd12{;! z7SP}Pl`Swtmc5GN;t~t9 zx6!CSkY`XJ2q3=V|&dXAPWcobVrj z_iu7*>#w+FB-(7f-I%b*q!|cpt)BuVv;fZTVh7~A=qS5#EkmhEMyuc}{apzxylo~| z0mbeS?tU`!LbR$4sQ>gBa%fKIzpEN`T#7Y0lv+IBV9+4E0}g@vY=STiGXx{Ehjw7R zKb93x$=p_kU!OwAbYO$fe9Tedl^MBh7b?AyoxC*=OkIFz++H|}X-JF*Ehae1+8 zM@mvq{Y+{lu}W3tzcGQ>p)}r=N&-;9Jpnm&b~7%&FpQR+{hOegT8U{oq*;X}%?1FK zh`psaJT z@cb3yd723I?b3D!M?&Bb|5zWJDf06 z0C6O1Q3E=TOumsrGl!7>w$12z=ft+>0ff9tpY=0C@z2*i>le`G(9~S+U7YZ>@&9oN zN3siaX61BCwDQ>u>S(5^)<%^UgvIk!?)g1TMw0izq?@y3{f2<&JsS=ElEd8`aON{0 zXsns|GtQ0pwd;V|BTWq#bRfZePck|#OsMM4P(>^9S*)IYaC*WnQ}gvFoP5wlk3eU@ zoulPAYNq~eD2<;~BK}3|gMAltfk$fG!2W=>rqdr`l}7>^m6ici zg0HjxLZxI#_u;i^A=S$LCb8D8@R2>*<2@IvLnX(6e&!qijU=IPuF5|C?^SvA?f%S& zG~6gb23z(kYq_zS<(=0v2j%>1H@fCDaC>5ZF%7dz=CS_ZjKLs;w_f^s^ym!x%`s(5 zRSKs>EwB?Ch}pEq+H>5|u0H%yewqOp?%}7Cbm?5A-VFaK*_$7?^p2w?bG@ZpB*pV? z1PB(KeK!B{<}TS8_T9C-pm2S~A$4?8F9J?BlQjIMT@+ zK*b4pNV8^vmfV9N2w`%|<<)g6?h#5|sU1YKzmW$kNxKC32O)vqi=6mZqJM%o4^36H zf0f7h-MZ%?pby}`3=ch(ldHZw0L#eCO>u43FCUcZKY?el?CQh!Bo_eB8EiL|rSuEVzvJWuf5zi#shxi!uoPx(zJ_*d^9PM(M zxf@Qc+WGLtDc0jcZx27Qk~9_}{hU|o!TWo>PA`u*~#`=SPr{F zO$qMXPAUQmNxwY<{YbwLmfq+C1rU#r$?S$9hPWR!KUU~b1|x=++sML)Q6=W(tuAaTn_wxl+s1de9o9pd+a)T77pCs4i)0zY5;GZLjK(6 zk6NkUOQwQJTnRCSWzC+AAY!P;suTHalp;7dd3f4?7|rV#a^+G9KdDKpsH+pCSetC{ zDY4HDynIy``3#RX>TvU_t8XVrr|)4VvIO2S-01dFr$Od;*XI24j9xWzs7W7B&~bu1 zR@ylG@m!Ef$gI>w24;qzww1H#BEkEbLPiEh0s!Jon3v)!Azlz5EgzrcG zINJ`34QR*`c8VGCI(ZiwdOqYBu>aZ5WM`IWUs-Y%>UH0}*mQCrUBV?q@BIZdsE6-h zd-iqLSz`8GVW;VeaOI*w`jzwDH_L}G!K;y}oNYwktYLYv2pO;V3XRElg{PH0UK24nw&HZ9Chx#A*4Tx@I+IWI zIj)c)7pb7frN`=NT#AD^XZN^U-Syy#uoL)lqReH*Y1VVg*DI4l^A#G%%vo>q{;vl0 zkLEO*PK3L;+fJ2vMBV#Gg4c(v=XI|7tMiKjRtFIBAPNN8?Mb{6-TW#Z1w)oxxHzNs zvj=ywH6{s9q&2ZeX^JCC^D+WkYPC{Hdf%fNkWs=M7d?PLv}BN2wzmEV&5m#kAhHeTZCG6 z;U0W@(%n{O6EIOb*>$tQl+)MVv!_Fi7e%0zL;TWt_~O`byo%pG!HdUM3NXUw*{Tik z20xC$^W`V?A|u2CCn-PrGVUuEEhhJ4L`P~$n(vLClQ;$|R6+LQQj8@@KhxK?K->N87Pj=g;%TQt{=1wgr3A=Cp31>JR@5hp zB?Z!*@`!Wl*43QP0w0v+A29Mkt5!o7G;AH)AOs zgM0j#M%I$9v{smwAUT~9m~i?ATet|0r?m5ls>p+%Cf+xRf%I4JLms(T0NsVuDgNz) zww^LNJ`t;;>~)ReY1<9ahVtnn)K2Zdn}?=wqu%dXr)vz3V+^9|_l)C!TU z$se{jGvDX{?MxfANVKdJ$>X{tWwG8T70WM|r=9E~2X{Z7vwX3YRcu-diQEf7_Ftaj zznJ1ZAIW>3|IkMJ{nFI(c-GX=7_ikz*Odx$GI3QZ_+0v!vt(=y*s0gP|t}|9DuBJv$uJ?5}x4u zHB<68R6Ch!H!?M6Sow$7P&YIt;vH=78GUK-$6L4HT_nAT@VzYQ>e%SjYu;>5x#ZI> z7`B6-(fdf=Uh2H+e|Bn_Xpv4V`UZ($V0ziS%T%fG#h6&fFf3zf^TSx-V zp?}HyyY$9#fY|>ZC*(Sd;Wh?>rT<$2_isUoyzIf++^iMzUxnR&1}iGpumBXnvU7g+ zkNf1WM}=VHQaa7NwG##IL^5JIPE^S0>cTrrj@|w>>N$@OZVc!nf2oBKzocVHOdwIg z#FFsY?)=*1p+7__(*7^nmt6rPaaC4YCAR%C8G*kt-xF`&`fCjeS+D}GadKM{_;8Ss zEC8GNc%pIeFDprMz?s6A8jtdL`q#BzW`}f+Ltf!|iG2Mk_m+wZ1Nc!@)KaLDw+j6~ Ds!}C0 literal 0 HcmV?d00001 diff --git a/docs/screenshots/vrm-portal_008.png b/docs/screenshots/vrm-portal_008.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b68581002b96941aad7a43e5328d0431f6276b GIT binary patch literal 55744 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�Bp!1{09DxgBd;{YJn7! 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 TE22Vx1bmd_)MYEAEkgef2ee5( literal 0 HcmV?d00001 diff --git a/docs/sidebars.js b/docs/sidebars.js new file mode 100644 index 00000000..5e973140 --- /dev/null +++ b/docs/sidebars.js @@ -0,0 +1,38 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [ + { + type: 'autogenerated', + dirName: '.' + } + ], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + 'intro', + 'hello', + { + type: 'category', + label: 'Tutorial', + items: ['tutorial-basics/create-a-document'], + }, + ], + */ +}; + +module.exports = sidebars; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css new file mode 100644 index 00000000..4db7db46 --- /dev/null +++ b/docs/src/css/custom.css @@ -0,0 +1,55 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +.hidden { + display: none !important; +} + +/* You can override the default Infima variables here. */ +:root { + --ifm-container-width-xl: 1780px; + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + --ifm-blockquote-border-left-width: 6px; + --ifm-blockquote-border-color: #ff5f5f; + --ifm-blockquote-padding-vertical: 0.5rem; + --ifm-blockquote-padding-horizontal: 1rem; +} + +blockquote { + background-color: #fff4f4; +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme="dark"] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} + +[data-theme="dark"] blockquote { + background-color: #372b2b; +} + +/* +@media (min-width: 1900px) { + .container { + max-width: 1780px; + } +} +*/ diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c01d54bcd39a5f853428f3cd5aa0f383d963c484 GIT binary patch literal 3626 zcmb`Je@s(X6vrR`EK3%b%orErlDW({vnABqA zcfaS{d+xbU5JKp0*;0YOg+;Fl!eT)XRuapIwFLL`=imZCSon$`se`_<%@MB=M~KG+ z=EW^FL`w|Bo>*ktlaS^(fut!95`iG5u=SZ8nfDHO#GaTlH1-XG^;vsjUb^gWTVz0+ z^=WR1wv9-2oeR=_;fL0H7rNWqAzGtO(D;`~cX(RcN0w2v24Y8)6t`cS^_ghs`_ho? z{0ka~1Dgo8TfAP$r*ua?>$_V+kZ!-(TvEJ7O2f;Y#tezt$&R4 zLI}=-y@Z!grf*h3>}DUL{km4R>ya_I5Ag#{h_&?+HpKS!;$x3LC#CqUQ8&nM?X))Q zXAy2?`YL4FbC5CgJu(M&Q|>1st8XXLZ|5MgwgjP$m_2Vt0(J z&Gu7bOlkbGzGm2sh?X`){7w69Y$1#@P@7DF{ZE=4%T0NDS)iH`tiPSKpDNW)zmtn( zw;4$f>k)4$LBc>eBAaTZeCM2(iD+sHlj!qd z2GjRJ>f_Qes(+mnzdA^NH?^NB(^o-%Gmg$c8MNMq&`vm@9Ut;*&$xSD)PKH{wBCEC z4P9%NQ;n2s59ffMn8*5)5AAg4-93gBXBDX`A7S& zH-|%S3Wd%T79fk-e&l`{!?lve8_epXhE{d3Hn$Cg!t=-4D(t$cK~7f&4s?t7wr3ZP z*!SRQ-+tr|e1|hbc__J`k3S!rMy<0PHy&R`v#aJv?`Y?2{avK5sQz%=Us()jcNuZV z*$>auD4cEw>;t`+m>h?f?%VFJZj8D|Y1e_SjxG%J4{-AkFtT2+ZZS5UScS~%;dp!V>)7zi`w(xwSd*FS;Lml=f6hn#jq)2is4nkp+aTrV?)F6N z>DY#SU0IZ;*?Hu%tSj4edd~kYNHMFvS&5}#3-M;mBCOCZL3&;2obdG?qZ>rD|zC|Lu|sny76pn2xl|6sk~Hs{X9{8iBW zwiwgQt+@hi`FYMEhX2 \ No newline at end of file diff --git a/docs/static/matomo.js b/docs/static/matomo.js new file mode 100644 index 00000000..17bbad91 --- /dev/null +++ b/docs/static/matomo.js @@ -0,0 +1,12 @@ +var _paq = window._paq = window._paq || []; +/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ +_paq.push(["disableCookies"]); +_paq.push(['trackPageView']); +_paq.push(['enableLinkTracking']); +(function() { + var u="//github.md0.eu/matomo/"; + _paq.push(['setTrackerUrl', u+'matomo.php']); + _paq.push(['setSiteId', '1']); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); +})(); diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 00000000..6f475698 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,7 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@tsconfig/docusaurus/tsconfig.json", + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 00000000..7a9d50f1 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,7615 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/autocomplete-core@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz#85ff36b2673654a393c8c505345eaedd6eaa4f70" + integrity sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg== + dependencies: + "@algolia/autocomplete-shared" "1.7.4" + +"@algolia/autocomplete-preset-algolia@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz#610ee1d887962f230b987cba2fd6556478000bc3" + integrity sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ== + dependencies: + "@algolia/autocomplete-shared" "1.7.4" + +"@algolia/autocomplete-shared@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz#78aea1140a50c4d193e1f06a13b7f12c5e2cbeea" + integrity sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg== + +"@algolia/cache-browser-local-storage@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.0.tgz#4c54a9b1795dcc1cd9f9533144f7df3057984d39" + integrity sha512-myRSRZDIMYB8uCkO+lb40YKiYHi0fjpWRtJpR/dgkaiBlSD0plRyB6lLOh1XIfmMcSeBOqDE7y9m8xZMrXYfyQ== + dependencies: + "@algolia/cache-common" "4.17.0" + +"@algolia/cache-common@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.17.0.tgz#bc3da15548df585b44d76c55e66b0056a2b3f917" + integrity sha512-g8mXzkrcUBIPZaulAuqE7xyHhLAYAcF2xSch7d9dABheybaU3U91LjBX6eJTEB7XVhEsgK4Smi27vWtAJRhIKQ== + +"@algolia/cache-in-memory@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.17.0.tgz#eb55a92cb8eb8641903a2b23fd6d05ebdaca2010" + integrity sha512-PT32ciC/xI8z919d0oknWVu3kMfTlhQn3MKxDln3pkn+yA7F7xrxSALysxquv+MhFfNAcrtQ/oVvQVBAQSHtdw== + dependencies: + "@algolia/cache-common" "4.17.0" + +"@algolia/client-account@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.17.0.tgz#4b13e5a8e50a06be1f3289d9db337096ebc66b73" + integrity sha512-sSEHx9GA6m7wrlsSMNBGfyzlIfDT2fkz2u7jqfCCd6JEEwmxt8emGmxAU/0qBfbhRSuGvzojoLJlr83BSZAKjA== + dependencies: + "@algolia/client-common" "4.17.0" + "@algolia/client-search" "4.17.0" + "@algolia/transporter" "4.17.0" + +"@algolia/client-analytics@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.17.0.tgz#1b36ffbe913b7b4d8900bc15982ca431f47a473c" + integrity sha512-84ooP8QA3mQ958hQ9wozk7hFUbAO+81CX1CjAuerxBqjKIInh1fOhXKTaku05O/GHBvcfExpPLIQuSuLYziBXQ== + dependencies: + "@algolia/client-common" "4.17.0" + "@algolia/client-search" "4.17.0" + "@algolia/requester-common" "4.17.0" + "@algolia/transporter" "4.17.0" + +"@algolia/client-common@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.17.0.tgz#67fd898006e3ac359ea3e3ed61abfc26147ffa53" + integrity sha512-jHMks0ZFicf8nRDn6ma8DNNsdwGgP/NKiAAL9z6rS7CymJ7L0+QqTJl3rYxRW7TmBhsUH40wqzmrG6aMIN/DrQ== + dependencies: + "@algolia/requester-common" "4.17.0" + "@algolia/transporter" "4.17.0" + +"@algolia/client-personalization@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.17.0.tgz#428d9f4762c22856b6062bb54351eb31834db6c1" + integrity sha512-RMzN4dZLIta1YuwT7QC9o+OeGz2cU6eTOlGNE/6RcUBLOU3l9tkCOdln5dPE2jp8GZXPl2yk54b2nSs1+pAjqw== + dependencies: + "@algolia/client-common" "4.17.0" + "@algolia/requester-common" "4.17.0" + "@algolia/transporter" "4.17.0" + +"@algolia/client-search@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.17.0.tgz#0053c682f5f588e006c20791c27e8bcb0aa5b53c" + integrity sha512-x4P2wKrrRIXszT8gb7eWsMHNNHAJs0wE7/uqbufm4tZenAp+hwU/hq5KVsY50v+PfwM0LcDwwn/1DroujsTFoA== + dependencies: + "@algolia/client-common" "4.17.0" + "@algolia/requester-common" "4.17.0" + "@algolia/transporter" "4.17.0" + +"@algolia/events@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" + integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + +"@algolia/logger-common@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.17.0.tgz#0fcea39c9485554edb4cdbfd965c5748b0b837ac" + integrity sha512-DGuoZqpTmIKJFDeyAJ7M8E/LOenIjWiOsg1XJ1OqAU/eofp49JfqXxbfgctlVZVmDABIyOz8LqEoJ6ZP4DTyvw== + +"@algolia/logger-console@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.17.0.tgz#8ac56ef4259c4fa3eb9eb6586c7b4b4ed942e8da" + integrity sha512-zMPvugQV/gbXUvWBCzihw6m7oxIKp48w37QBIUu/XqQQfxhjoOE9xyfJr1KldUt5FrYOKZJVsJaEjTsu+bIgQg== + dependencies: + "@algolia/logger-common" "4.17.0" + +"@algolia/requester-browser-xhr@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.0.tgz#f52fdeeac2f3c531f00838920af33a73066a159b" + integrity sha512-aSOX/smauyTkP21Pf52pJ1O2LmNFJ5iHRIzEeTh0mwBeADO4GdG94cAWDILFA9rNblq/nK3EDh3+UyHHjplZ1A== + dependencies: + "@algolia/requester-common" "4.17.0" + +"@algolia/requester-common@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.17.0.tgz#746020d2cbc829213e7cede8eef2182c7a71e32b" + integrity sha512-XJjmWFEUlHu0ijvcHBoixuXfEoiRUdyzQM6YwTuB8usJNIgShua8ouFlRWF8iCeag0vZZiUm4S2WCVBPkdxFgg== + +"@algolia/requester-node-http@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.17.0.tgz#262276d94c25a4ec2128b1bdfb9471529528d8b9" + integrity sha512-bpb/wDA1aC6WxxM8v7TsFspB7yBN3nqCGs2H1OADolQR/hiAIjAxusbuMxVbRFOdaUvAIqioIIkWvZdpYNIn8w== + dependencies: + "@algolia/requester-common" "4.17.0" + +"@algolia/transporter@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.17.0.tgz#6aabdbc20c475d72d83c8e6519f1191f1a51fb37" + integrity sha512-6xL6H6fe+Fi0AEP3ziSgC+G04RK37iRb4uUUqVAH9WPYFI8g+LYFq6iv5HS8Cbuc5TTut+Bwj6G+dh/asdb9uA== + dependencies: + "@algolia/cache-common" "4.17.0" + "@algolia/logger-common" "4.17.0" + "@algolia/requester-common" "4.17.0" + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.8.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.18.6", "@babel/core@^7.19.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" + integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== + dependencies: + "@babel/types" "^7.21.0" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.20.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-arrow-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" + +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-for-of@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + dependencies: + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-modules-commonjs@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + dependencies: + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" + +"@babel/plugin-transform-modules-systemjs@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-constant-elements@^7.18.12": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz#b32a5556100d424b25e388dd689050d78396884d" + integrity sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.21.0" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-runtime@^7.18.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== + dependencies: + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.18.6", "@babel/preset-env@^7.19.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.4.tgz#a952482e634a8dd8271a3fe5459a16eb10739c58" + integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.20.7" + "@babel/plugin-transform-async-to-generator" "^7.20.7" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.20.7" + "@babel/plugin-transform-destructuring" "^7.21.3" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.21.0" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.20.7" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.21.4" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.18.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" + integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-typescript" "^7.21.3" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime-corejs3@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz#6e4939d9d9789ff63e2dc58e88f13a3913a24eba" + integrity sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw== + dependencies: + core-js-pure "^3.25.1" + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.8.4": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.12.7", "@babel/template@^7.18.10", "@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.4.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@0.5.7": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@docsearch/css@3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.3.tgz#f9346c9e24602218341f51b8ba91eb9109add434" + integrity sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg== + +"@docsearch/react@^3.1.1": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.3.tgz#907b6936a565f880b4c0892624b4f7a9f132d298" + integrity sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q== + dependencies: + "@algolia/autocomplete-core" "1.7.4" + "@algolia/autocomplete-preset-algolia" "1.7.4" + "@docsearch/css" "3.3.3" + algoliasearch "^4.0.0" + +"@docusaurus/core@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.0.tgz#a12c175cb2e5a7e4582e65876a50813f6168913d" + integrity sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA== + dependencies: + "@babel/core" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-runtime" "^7.18.6" + "@babel/preset-env" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/runtime" "^7.18.6" + "@babel/runtime-corejs3" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.4.0" + "@docusaurus/logger" "2.4.0" + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-common" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + "@slorber/static-site-generator-webpack-plugin" "^4.0.7" + "@svgr/webpack" "^6.2.1" + autoprefixer "^10.4.7" + babel-loader "^8.2.5" + babel-plugin-dynamic-import-node "^2.3.3" + boxen "^6.2.1" + chalk "^4.1.2" + chokidar "^3.5.3" + clean-css "^5.3.0" + cli-table3 "^0.6.2" + combine-promises "^1.1.0" + commander "^5.1.0" + copy-webpack-plugin "^11.0.0" + core-js "^3.23.3" + css-loader "^6.7.1" + css-minimizer-webpack-plugin "^4.0.0" + cssnano "^5.1.12" + del "^6.1.1" + detect-port "^1.3.0" + escape-html "^1.0.3" + eta "^2.0.0" + file-loader "^6.2.0" + fs-extra "^10.1.0" + html-minifier-terser "^6.1.0" + html-tags "^3.2.0" + html-webpack-plugin "^5.5.0" + import-fresh "^3.3.0" + leven "^3.1.0" + lodash "^4.17.21" + mini-css-extract-plugin "^2.6.1" + postcss "^8.4.14" + postcss-loader "^7.0.0" + prompts "^2.4.2" + react-dev-utils "^12.0.1" + react-helmet-async "^1.3.0" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + react-loadable-ssr-addon-v5-slorber "^1.0.1" + react-router "^5.3.3" + react-router-config "^5.1.1" + react-router-dom "^5.3.3" + rtl-detect "^1.0.4" + semver "^7.3.7" + serve-handler "^6.1.3" + shelljs "^0.8.5" + terser-webpack-plugin "^5.3.3" + tslib "^2.4.0" + update-notifier "^5.1.0" + url-loader "^4.1.1" + wait-on "^6.0.1" + webpack "^5.73.0" + webpack-bundle-analyzer "^4.5.0" + webpack-dev-server "^4.9.3" + webpack-merge "^5.8.0" + webpackbar "^5.0.2" + +"@docusaurus/cssnano-preset@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.0.tgz#9213586358e0cce517f614af041eb7d184f8add6" + integrity sha512-RmdiA3IpsLgZGXRzqnmTbGv43W4OD44PCo+6Q/aYjEM2V57vKCVqNzuafE94jv0z/PjHoXUrjr69SaRymBKYYw== + dependencies: + cssnano-preset-advanced "^5.3.8" + postcss "^8.4.14" + postcss-sort-media-queries "^4.2.1" + tslib "^2.4.0" + +"@docusaurus/logger@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.4.0.tgz#393d91ad9ecdb9a8f80167dd6a34d4b45219b835" + integrity sha512-T8+qR4APN+MjcC9yL2Es+xPJ2923S9hpzDmMtdsOcUGLqpCGBbU1vp3AAqDwXtVgFkq+NsEk7sHdVsfLWR/AXw== + dependencies: + chalk "^4.1.2" + tslib "^2.4.0" + +"@docusaurus/mdx-loader@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz#c6310342904af2f203e7df86a9df623f86840f2d" + integrity sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g== + dependencies: + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@mdx-js/mdx" "^1.6.22" + escape-html "^1.0.3" + file-loader "^6.2.0" + fs-extra "^10.1.0" + image-size "^1.0.1" + mdast-util-to-string "^2.0.0" + remark-emoji "^2.2.0" + stringify-object "^3.3.0" + tslib "^2.4.0" + unified "^9.2.2" + unist-util-visit "^2.0.3" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@docusaurus/module-type-aliases@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.0.tgz#6961605d20cd46f86163ed8c2d83d438b02b4028" + integrity sha512-YEQO2D3UXs72qCn8Cr+RlycSQXVGN9iEUyuHwTuK4/uL/HFomB2FHSU0vSDM23oLd+X/KibQ3Ez6nGjQLqXcHg== + dependencies: + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/types" "2.4.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + "@types/react-router-dom" "*" + react-helmet-async "*" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + +"@docusaurus/plugin-content-blog@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.0.tgz#50dbfbc7b51f152ae660385fd8b34076713374c3" + integrity sha512-YwkAkVUxtxoBAIj/MCb4ohN0SCtHBs4AS75jMhPpf67qf3j+U/4n33cELq7567hwyZ6fMz2GPJcVmctzlGGThQ== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/logger" "2.4.0" + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-common" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + cheerio "^1.0.0-rc.12" + feed "^4.2.2" + fs-extra "^10.1.0" + lodash "^4.17.21" + reading-time "^1.5.0" + tslib "^2.4.0" + unist-util-visit "^2.0.3" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-docs@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.0.tgz#36e235adf902325735b873b4f535205884363728" + integrity sha512-ic/Z/ZN5Rk/RQo+Io6rUGpToOtNbtPloMR2JcGwC1xT2riMu6zzfSwmBi9tHJgdXH6CB5jG+0dOZZO8QS5tmDg== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/logger" "2.4.0" + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/module-type-aliases" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + "@types/react-router-config" "^5.0.6" + combine-promises "^1.1.0" + fs-extra "^10.1.0" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-pages@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.0.tgz#6169909a486e1eae0ddffff0b1717ce4332db4d4" + integrity sha512-Pk2pOeOxk8MeU3mrTU0XLIgP9NZixbdcJmJ7RUFrZp1Aj42nd0RhIT14BGvXXyqb8yTQlk4DmYGAzqOfBsFyGw== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + fs-extra "^10.1.0" + tslib "^2.4.0" + webpack "^5.73.0" + +"@docusaurus/plugin-debug@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.0.tgz#1ad513fe9bcaf017deccf62df8b8843faeeb7d37" + integrity sha512-KC56DdYjYT7Txyux71vXHXGYZuP6yYtqwClvYpjKreWIHWus5Zt6VNi23rMZv3/QKhOCrN64zplUbdfQMvddBQ== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + fs-extra "^10.1.0" + react-json-view "^1.21.3" + tslib "^2.4.0" + +"@docusaurus/plugin-google-analytics@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.0.tgz#8062d7a09d366329dfd3ce4e8a619da8624b6cc3" + integrity sha512-uGUzX67DOAIglygdNrmMOvEp8qG03X20jMWadeqVQktS6nADvozpSLGx4J0xbkblhJkUzN21WiilsP9iVP+zkw== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + tslib "^2.4.0" + +"@docusaurus/plugin-google-gtag@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz#a8efda476f971410dfb3aab1cfe1f0f7d269adc5" + integrity sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + tslib "^2.4.0" + +"@docusaurus/plugin-google-tag-manager@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.0.tgz#9a94324ac496835fc34e233cc60441df4e04dfdd" + integrity sha512-E66uGcYs4l7yitmp/8kMEVQftFPwV9iC62ORh47Veqzs6ExwnhzBkJmwDnwIysHBF1vlxnzET0Fl2LfL5fRR3A== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + tslib "^2.4.0" + +"@docusaurus/plugin-sitemap@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.0.tgz#ba0eb43565039fe011bdd874b5c5d7252b19d709" + integrity sha512-pZxh+ygfnI657sN8a/FkYVIAmVv0CGk71QMKqJBOfMmDHNN1FeDeFkBjWP49ejBqpqAhjufkv5UWq3UOu2soCw== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/logger" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-common" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + fs-extra "^10.1.0" + sitemap "^7.1.1" + tslib "^2.4.0" + +"@docusaurus/preset-classic@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.4.0.tgz#92fdcfab35d8d0ffb8c38bcbf439e4e1cb0566a3" + integrity sha512-/5z5o/9bc6+P5ool2y01PbJhoGddEGsC0ej1MF6mCoazk8A+kW4feoUd68l7Bnv01rCnG3xy7kHUQP97Y0grUA== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/plugin-content-blog" "2.4.0" + "@docusaurus/plugin-content-docs" "2.4.0" + "@docusaurus/plugin-content-pages" "2.4.0" + "@docusaurus/plugin-debug" "2.4.0" + "@docusaurus/plugin-google-analytics" "2.4.0" + "@docusaurus/plugin-google-gtag" "2.4.0" + "@docusaurus/plugin-google-tag-manager" "2.4.0" + "@docusaurus/plugin-sitemap" "2.4.0" + "@docusaurus/theme-classic" "2.4.0" + "@docusaurus/theme-common" "2.4.0" + "@docusaurus/theme-search-algolia" "2.4.0" + "@docusaurus/types" "2.4.0" + +"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== + dependencies: + "@types/react" "*" + prop-types "^15.6.2" + +"@docusaurus/theme-classic@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.4.0.tgz#a5404967b00adec3472efca4c3b3f6a5e2021c78" + integrity sha512-GMDX5WU6Z0OC65eQFgl3iNNEbI9IMJz9f6KnOyuMxNUR6q0qVLsKCNopFUDfFNJ55UU50o7P7o21yVhkwpfJ9w== + dependencies: + "@docusaurus/core" "2.4.0" + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/module-type-aliases" "2.4.0" + "@docusaurus/plugin-content-blog" "2.4.0" + "@docusaurus/plugin-content-docs" "2.4.0" + "@docusaurus/plugin-content-pages" "2.4.0" + "@docusaurus/theme-common" "2.4.0" + "@docusaurus/theme-translations" "2.4.0" + "@docusaurus/types" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-common" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + "@mdx-js/react" "^1.6.22" + clsx "^1.2.1" + copy-text-to-clipboard "^3.0.1" + infima "0.2.0-alpha.43" + lodash "^4.17.21" + nprogress "^0.2.0" + postcss "^8.4.14" + prism-react-renderer "^1.3.5" + prismjs "^1.28.0" + react-router-dom "^5.3.3" + rtlcss "^3.5.0" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-common@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.4.0.tgz#626096fe9552d240a2115b492c7e12099070cf2d" + integrity sha512-IkG/l5f/FLY6cBIxtPmFnxpuPzc5TupuqlOx+XDN+035MdQcAh8wHXXZJAkTeYDeZ3anIUSUIvWa7/nRKoQEfg== + dependencies: + "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/module-type-aliases" "2.4.0" + "@docusaurus/plugin-content-blog" "2.4.0" + "@docusaurus/plugin-content-docs" "2.4.0" + "@docusaurus/plugin-content-pages" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-common" "2.4.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + clsx "^1.2.1" + parse-numeric-range "^1.3.0" + prism-react-renderer "^1.3.5" + tslib "^2.4.0" + use-sync-external-store "^1.2.0" + utility-types "^3.10.0" + +"@docusaurus/theme-search-algolia@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.0.tgz#07d297d50c44446d6bc5a37be39afb8f014084e1" + integrity sha512-pPCJSCL1Qt4pu/Z0uxBAuke0yEBbxh0s4fOvimna7TEcBLPq0x06/K78AaABXrTVQM6S0vdocFl9EoNgU17hqA== + dependencies: + "@docsearch/react" "^3.1.1" + "@docusaurus/core" "2.4.0" + "@docusaurus/logger" "2.4.0" + "@docusaurus/plugin-content-docs" "2.4.0" + "@docusaurus/theme-common" "2.4.0" + "@docusaurus/theme-translations" "2.4.0" + "@docusaurus/utils" "2.4.0" + "@docusaurus/utils-validation" "2.4.0" + algoliasearch "^4.13.1" + algoliasearch-helper "^3.10.0" + clsx "^1.2.1" + eta "^2.0.0" + fs-extra "^10.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-translations@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.4.0.tgz#62dacb7997322f4c5a828b3ab66177ec6769eb33" + integrity sha512-kEoITnPXzDPUMBHk3+fzEzbopxLD3fR5sDoayNH0vXkpUukA88/aDL1bqkhxWZHA3LOfJ3f0vJbOwmnXW5v85Q== + dependencies: + fs-extra "^10.1.0" + tslib "^2.4.0" + +"@docusaurus/types@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.4.0.tgz#f94f89a0253778b617c5d40ac6f16b17ec55ce41" + integrity sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + commander "^5.1.0" + joi "^17.6.0" + react-helmet-async "^1.3.0" + utility-types "^3.10.0" + webpack "^5.73.0" + webpack-merge "^5.8.0" + +"@docusaurus/utils-common@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.4.0.tgz#eb2913871860ed32e73858b4c7787dd820c5558d" + integrity sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A== + dependencies: + tslib "^2.4.0" + +"@docusaurus/utils-validation@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz#1ed92bfab5da321c4a4d99cad28a15627091aa90" + integrity sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w== + dependencies: + "@docusaurus/logger" "2.4.0" + "@docusaurus/utils" "2.4.0" + joi "^17.6.0" + js-yaml "^4.1.0" + tslib "^2.4.0" + +"@docusaurus/utils@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.4.0.tgz#fdf0c3545819e48bb57eafc5057495fd4d50e900" + integrity sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg== + dependencies: + "@docusaurus/logger" "2.4.0" + "@svgr/webpack" "^6.2.1" + escape-string-regexp "^4.0.0" + file-loader "^6.2.0" + fs-extra "^10.1.0" + github-slugger "^1.4.0" + globby "^11.1.0" + gray-matter "^4.0.3" + js-yaml "^4.1.0" + lodash "^4.17.21" + micromatch "^4.0.5" + resolve-pathname "^3.0.0" + shelljs "^0.8.5" + tslib "^2.4.0" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" + integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@slorber/static-site-generator-webpack-plugin@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" + integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== + dependencies: + eval "^0.1.8" + p-map "^4.0.0" + webpack-sources "^3.2.2" + +"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" + integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ== + +"@svgr/babel-plugin-remove-jsx-attribute@*": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz#91da77a009dc38e8d30da45d9b62ef8736f2d90a" + integrity sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ== + +"@svgr/babel-plugin-remove-jsx-empty-expression@*": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz#5154ff1213509e36ab315974c8c2fd48dafb827b" + integrity sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz#fb9d22ea26d2bc5e0a44b763d4c46d5d3f596c60" + integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg== + +"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz#01b2024a2b53ffaa5efceaa0bf3e1d5a4c520ce4" + integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw== + +"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz#dd3fa9f5b24eb4f93bcf121c3d40ff5facecb217" + integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz#1d8e945a03df65b601551097d8f5e34351d3d305" + integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg== + +"@svgr/babel-plugin-transform-svg-component@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz#48620b9e590e25ff95a80f811544218d27f8a250" + integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ== + +"@svgr/babel-preset@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.5.1.tgz#b90de7979c8843c5c580c7e2ec71f024b49eb828" + integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" + "@svgr/babel-plugin-remove-jsx-attribute" "*" + "@svgr/babel-plugin-remove-jsx-empty-expression" "*" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" + "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" + "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" + "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" + "@svgr/babel-plugin-transform-svg-component" "^6.5.1" + +"@svgr/core@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.5.1.tgz#d3e8aa9dbe3fbd747f9ee4282c1c77a27410488a" + integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz#81800bd09b5bcdb968bf6ee7c863d2288fdb80d2" + integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw== + dependencies: + "@babel/types" "^7.20.0" + entities "^4.4.0" + +"@svgr/plugin-jsx@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz#0e30d1878e771ca753c94e69581c7971542a7072" + integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/hast-util-to-babel-ast" "^6.5.1" + svg-parser "^2.0.4" + +"@svgr/plugin-svgo@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz#0f91910e988fc0b842f88e0960c2862e022abe84" + integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.8.0" + +"@svgr/webpack@^6.2.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.5.1.tgz#ecf027814fc1cb2decc29dc92f39c3cf691e40e8" + integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA== + dependencies: + "@babel/core" "^7.19.6" + "@babel/plugin-transform-react-constant-elements" "^7.18.12" + "@babel/preset-env" "^7.19.4" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@svgr/core" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + "@svgr/plugin-svgo" "^6.5.1" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tsconfig/docusaurus@^1.0.5": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/docusaurus/-/docusaurus-1.0.7.tgz#a3ee3c8109b3fec091e3d61a61834e563aeee3c3" + integrity sha512-ffTXxGIP/IRMCjuzHd6M4/HdIrw1bMfC7Bv8hMkTadnePkpe0lG0oDSdbRpSDZb2rQMAgpbWiR10BvxvNYwYrg== + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.37.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.37.0.tgz#29cebc6c2a3ac7fea7113207bf5a828fdf4d7ef1" + integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/hast@^2.0.0": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" + integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + dependencies: + "@types/unist" "*" + +"@types/history@^4.7.11": + version "4.7.11" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" + integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-proxy@^1.17.8": + version "1.17.10" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.10.tgz#e576c8e4a0cc5c6a138819025a88e167ebb38d6c" + integrity sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/mdast@^3.0.0": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.11.tgz#dc130f7e7d9306124286f6d6cee40cf4d14a3dc0" + integrity sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw== + dependencies: + "@types/unist" "*" + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/node@*": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + +"@types/node@^17.0.5": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-router-config@*", "@types/react-router-config@^5.0.6": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.7.tgz#36207a3fe08b271abee62b26993ee932d13cbb02" + integrity sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "^5.1.0" + +"@types/react-router-dom@*": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" + integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*", "@types/react-router@^5.1.0": + version "5.1.20" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c" + integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + +"@types/react@*": + version "18.0.35" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741" + integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/sax@^1.2.1": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" + integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== + dependencies: + "@types/node" "*" + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" + integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@types/ws@^8.5.1": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-walk@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +address@^1.0.1, address@^1.1.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +algoliasearch-helper@^3.10.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.12.0.tgz#0fe39d49b0290e4aa5e1fe733bd24d857d258e94" + integrity sha512-/j1U3PEwdan0n6P/QqSnSpNSLC5+cEMvyljd5CnmNmUjDlGrys+vFEOwjVEnqELIiAGMHEA/Nl3CiKVFBUYqyQ== + dependencies: + "@algolia/events" "^4.0.1" + +algoliasearch@^4.0.0, algoliasearch@^4.13.1: + version "4.17.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.17.0.tgz#46ed58b2b99509d041f11cd1ea83623edf84355f" + integrity sha512-JMRh2Mw6sEnVMiz6+APsi7lx9a2jiDFF+WUtANaUVCv6uSU9UOLdo5h9K3pdP6frRRybaM2fX8b1u0nqICS9aA== + dependencies: + "@algolia/cache-browser-local-storage" "4.17.0" + "@algolia/cache-common" "4.17.0" + "@algolia/cache-in-memory" "4.17.0" + "@algolia/client-account" "4.17.0" + "@algolia/client-analytics" "4.17.0" + "@algolia/client-common" "4.17.0" + "@algolia/client-personalization" "4.17.0" + "@algolia/client-search" "4.17.0" + "@algolia/logger-common" "4.17.0" + "@algolia/logger-console" "4.17.0" + "@algolia/requester-browser-xhr" "4.17.0" + "@algolia/requester-common" "4.17.0" + "@algolia/requester-node-http" "4.17.0" + "@algolia/transporter" "4.17.0" + +ansi-align@^3.0.0, ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +autoprefixer@^10.4.12, autoprefixer@^10.4.7: + version "10.4.14" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" + integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== + dependencies: + browserslist "^4.21.5" + caniuse-lite "^1.0.30001464" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +babel-loader@^8.2.5: + version "8.3.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" + integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.1.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.1.tgz#960948fa0e0153f5d26743ab15baf8e33752c135" + integrity sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +boxen@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-6.2.1.tgz#b098a2278b2cd2845deef2dff2efc38d329b434d" + integrity sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw== + dependencies: + ansi-align "^3.0.1" + camelcase "^6.2.0" + chalk "^4.1.2" + cli-boxes "^3.0.0" + string-width "^5.0.1" + type-fest "^2.5.0" + widest-line "^4.0.1" + wrap-ansi "^8.0.1" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: + version "1.0.30001478" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" + integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@^1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + +chokidar@^3.4.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +clean-css@^5.2.2, clean-css@^5.3.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" + integrity sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-table3@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.10: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +combine-promises@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" + integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== + +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +copy-text-to-clipboard@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz#6bf40deef0a51ac6858efb0d76ded2c6d6a15059" + integrity sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng== + +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +core-js-compat@^3.25.1: + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" + integrity sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw== + dependencies: + browserslist "^4.21.5" + +core-js-pure@^3.25.1: + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.30.1.tgz#7d93dc89e7d47b8ef05d7e79f507b0e99ea77eec" + integrity sha512-nXBEVpmUnNRhz83cHd9JRQC52cTMcuXAmR56+9dSMpRdpeA4I1PX6yjmhd71Eyc/wXNsdBdUDIj1QTIeZpU5Tg== + +core-js@^3.23.3: + version "3.30.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.1.tgz#fc9c5adcc541d8e9fa3e381179433cbf795628ba" + integrity sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig-typescript-loader@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" + integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cosmiconfig@^8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689" + integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + +cross-fetch@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-declaration-sorter@^6.3.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz#630618adc21724484b3e9505bce812def44000ad" + integrity sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew== + +css-loader@^6.7.1: + version "6.7.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd" + integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.19" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.8" + +css-minimizer-webpack-plugin@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" + integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== + dependencies: + cssnano "^5.1.8" + jest-worker "^29.1.2" + postcss "^8.4.17" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-advanced@^5.3.8: + version "5.3.10" + resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz#25558a1fbf3a871fb6429ce71e41be7f5aca6eef" + integrity sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ== + dependencies: + autoprefixer "^10.4.12" + cssnano-preset-default "^5.2.14" + postcss-discard-unused "^5.1.0" + postcss-merge-idents "^5.1.1" + postcss-reduce-idents "^5.2.0" + postcss-zindex "^5.1.0" + +cssnano-preset-default@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" + integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== + dependencies: + css-declaration-sorter "^6.3.1" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.1" + postcss-convert-values "^5.1.3" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.7" + postcss-merge-rules "^5.1.4" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.4" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.1" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.2" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.1.12, cssnano@^5.1.8: + version "5.1.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" + integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== + dependencies: + cssnano-preset-default "^5.2.14" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +debug@2.6.9, debug@^2.6.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +del@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-port-alt@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +detect-port@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== + dependencies: + address "^1.0.1" + debug "4" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.5.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.5.0.tgz#f59cbf3396c130957c56a6ad5fd3959ccdc30065" + integrity sha512-USawdAUzRkV6xrqTjiAEp6M9YagZEzWcSUaZTcIFAiyQWW1SoI6KyId8y2+/71wbgHKQAKd+iupLv4YvEwYWvA== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +duplexer3@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.284: + version "1.4.363" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.363.tgz#a3d51d16169d8f212f00bafb78cf0667e24c3647" + integrity sha512-ReX5qgmSU7ybhzMuMdlJAdYnRhT90UB3k9M05O5nF5WH3wR5wgdJjXw0uDeFyKNhmglmQiOxkAbzrP0hMKM59g== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +emoticon@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" + integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-module-lexer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" + integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eta@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/eta/-/eta-2.0.1.tgz#199e675359cb6e19d38f29e1f405e1ba0e79a6df" + integrity sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eval@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.8.tgz#2b903473b8cc1d1989b83a1e7923f883eb357f85" + integrity sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw== + dependencies: + "@types/node" "*" + require-like ">= 0.1.1" + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.11, fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0, fbjs@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" + integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== + dependencies: + cross-fetch "^3.1.5" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.30" + +feed@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" + integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== + dependencies: + xml-js "^1.6.11" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +filesize@^8.0.6: + version "8.0.7" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" + integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flux@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.4.tgz#9661182ea81d161ee1a6a6af10d20485ef2ac572" + integrity sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + +follow-redirects@^1.0.0, follow-redirects@^1.14.7: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" + integrity sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-monkey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +github-slugger@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" + integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.1: + version "13.1.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" + integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-minifier-terser@^6.0.2, html-minifier-terser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +image-size@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486" + integrity sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg== + dependencies: + queue "6.0.2" + +immer@^9.0.7: + version "9.0.21" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" + integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== + +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infima@0.2.0-alpha.43: + version "0.2.0-alpha.43" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.43.tgz#f7aa1d7b30b6c08afef441c726bac6150228cbe0" + integrity sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.5, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + dependencies: + has "^1.0.3" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.1.2: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== + dependencies: + "@types/node" "*" + jest-util "^29.5.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +joi@^17.6.0: + version "17.9.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" + integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.1.2, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +launch-editor@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7" + integrity sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.7.3" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lilconfig@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" + integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.1.2, memfs@^3.4.3: + version "3.5.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.0.tgz#9da86405fca0a539addafd37dbd452344fd1c0bd" + integrity sha512-yK6o8xVJlQerz57kvPROwTMgx5WtGwC2ZxDtOUsnGl49rHjYkfQoPNZPCKH73VdLE1BwBu/+Fx/NL8NYMUw2aA== + dependencies: + fs-monkey "^1.0.3" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mini-css-extract-plugin@^2.6.1: + version "2.7.5" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz#afbb344977659ec0f1f6e050c7aea456b121cfc5" + integrity sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ== + dependencies: + schema-utils "^4.0.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-numeric-range@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" + integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== + +parse5-htmlparser2-tree-adapter@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" + integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== + dependencies: + domhandler "^5.0.2" + parse5 "^7.0.0" + +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" + integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" + integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== + +postcss-discard-unused@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz#8974e9b143d887677304e558c1166d3762501142" + integrity sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-loader@^7.0.0: + version "7.2.4" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.2.4.tgz#2884f4ca172de633b2cf1f93dc852968f0632ba9" + integrity sha512-F88rpxxNspo5hatIc+orYwZDtHFaVFOSIVAx+fBfJC1GmhWbVmPWtmg2gXKE1OxJbneOSGn8PWdIwsZFcruS+w== + dependencies: + cosmiconfig "^8.1.3" + cosmiconfig-typescript-loader "^4.3.0" + klona "^2.0.6" + semver "^7.3.8" + +postcss-merge-idents@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz#7753817c2e0b75d0853b56f78a89771e15ca04a1" + integrity sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-merge-longhand@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" + integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.1" + +postcss-merge-rules@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" + integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" + integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== + dependencies: + browserslist "^4.21.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" + integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-reduce-idents@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz#c89c11336c432ac4b28792f24778859a67dfba95" + integrity sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" + integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.0.11" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sort-media-queries@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz#f48a77d6ce379e86676fc3f140cf1b10a06f6051" + integrity sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg== + dependencies: + sort-css-media-queries "2.1.0" + +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss-zindex@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.1.0.tgz#4a5c7e5ff1050bd4c01d95b1847dfdcc58a496ff" + integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== + +postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.19: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prism-react-renderer@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085" + integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== + +prismjs@^1.28.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.6.2, prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@1.2.8, rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ== + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" + integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== + dependencies: + "@babel/code-frame" "^7.16.0" + address "^1.1.2" + browserslist "^4.18.1" + chalk "^4.1.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^4.0.0" + filesize "^8.0.6" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.5.0" + global-modules "^2.0.0" + globby "^11.0.4" + gzip-size "^6.0.0" + immer "^9.0.7" + is-root "^2.1.0" + loader-utils "^3.2.0" + open "^8.4.0" + pkg-up "^3.1.0" + prompts "^2.4.2" + react-error-overlay "^6.0.11" + recursive-readdir "^2.2.2" + shell-quote "^1.7.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +react-dom@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== + +react-fast-compare@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.1.tgz#53933d9e14f364281d6cba24bfed7a4afb808b5f" + integrity sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg== + +react-helmet-async@*, react-helmet-async@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== + dependencies: + "@babel/runtime" "^7.12.5" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^3.2.0" + shallowequal "^1.1.0" + +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-loadable-ssr-addon-v5-slorber@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" + integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== + dependencies: + "@babel/runtime" "^7.10.3" + +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" + +react-router-dom@^5.3.3: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6" + integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.3.4" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.3.4, react-router@^5.3.3: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.4.tgz#8ca252d70fcc37841e31473c7a151cf777887bb5" + integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-textarea-autosize@^8.3.2: + version "8.4.1" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz#bcfc5462727014b808b14ee916c01e275e8a8335" + integrity sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q== + dependencies: + "@babel/runtime" "^7.20.13" + use-composed-ref "^1.3.0" + use-latest "^1.2.1" + +react@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +registry-auth-token@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" + integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== + dependencies: + rc "1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-emoji@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" + integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== + dependencies: + emoticon "^3.2.0" + node-emoji "^1.10.0" + unist-util-visit "^2.0.3" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-string@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +"require-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + +resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== + dependencies: + lowercase-keys "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rtl-detect@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" + integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== + +rtlcss@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" + integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== + dependencies: + find-up "^5.0.0" + picocolors "^1.0.0" + postcss "^8.3.11" + strip-json-comments "^3.1.1" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.5.4: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: + version "7.4.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" + integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-handler@^6.1.3: + version "6.1.5" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" + integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.1.2" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" + integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sort-css-media-queries@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce" + integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +std-env@^3.0.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955" + integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA== + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +stylehacks@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" + integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== + dependencies: + browserslist "^4.21.4" + postcss-selector-parser "^6.0.4" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^2.7.0, svgo@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.3, terser-webpack-plugin@^5.3.7: + version "5.3.7" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz#ef760632d24991760f339fe9290deb936ad1ffc7" + integrity sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.5" + +terser@^5.10.0, terser@^5.16.5: + version "5.16.9" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.9.tgz#7a28cb178e330c484369886f2afd623d9847495f" + integrity sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tiny-invariant@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + +tiny-warning@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^2.5.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@^4.7.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +ua-parser-js@^0.7.30: + version "0.7.35" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307" + integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unified@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" + integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== + dependencies: + prepend-http "^2.0.0" + +use-composed-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" + integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== + +use-isomorphic-layout-effect@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + +use-latest@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2" + integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +wait-on@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-bundle-analyzer@^4.5.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04" + integrity sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg== + dependencies: + "@discoveryjs/json-ext" "0.5.7" + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.9.3: + version "4.13.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz#d97445481d78691efe6d9a3b230833d802fc31f9" + integrity sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + +webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.2, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.73.0: + version "5.79.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.79.0.tgz#8552b5da5a26e4e25842c08a883e08fc7740547a" + integrity sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +webpackbar@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" + integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== + dependencies: + chalk "^4.1.0" + consola "^2.15.3" + pretty-time "^1.1.0" + std-env "^3.0.1" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.3.1: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== From 557b9ac0ad1930d369253cecc2ef7d2e4da11662 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 22:51:54 +0200 Subject: [PATCH 158/209] Merge branch 'master' of into jkbms_ble --- .github/workflows/github-pages.yml | 4 +- README.md | 61 ++++++++++++++++------ etc/dbus-serialbattery/bms/hlpdatabms4s.py | 2 +- etc/dbus-serialbattery/bms/seplos.py | 11 ++-- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 0b30880c..158b8843 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -5,7 +5,9 @@ on: # Runs on pushes targeting the default branch push: # Run on changes in the master branch - #branches: [master]: + branches: + - master + - docusaurus # Run on changes in the docs folder paths: diff --git a/README.md b/README.md index 243e7365..e2989624 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,66 @@ # dbus-serialbattery -This is a driver for VenusOS devices (any GX device sold by Victron or a Raspberry Pi running the VenusOS image). +This is a driver for Venus OS devices (any GX device sold by Victron or a Raspberry Pi running the Venus OS image). -The driver will communicate with a Battery Management System (BMS) that support serial communication (RS232, RS485 or TTL UART) and publish this data to the VenusOS system. The main purpose is to act as a Battery Monitor in your GX and supply State of Charge (SoC) and other values to the inverter. +The driver will communicate with a Battery Management System (BMS) that support serial communication (RS232, RS485 or TTL UART) and publish this data to the Venus OS system. The main purpose is to act as a Battery Monitor in your GX and supply State Of Charge (SOC) and other values to the inverter. -## Documentation -Check the documenation for more informations. -* [Introduction](https://louisvdw.github.io/dbus-serialbattery/) - * [Features](https://louisvdw.github.io/dbus-serialbattery/general/features) - * [Supported BMS](https://louisvdw.github.io/dbus-serialbattery/general/supported-bms) - * [How to install](https://louisvdw.github.io/dbus-serialbattery/general/install) -* [Troubleshoot](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/) - * [FAQ (Frequently Asked Questions)](https://louisvdw.github.io/dbus-serialbattery/troubleshoot/faq) + * [BMS Types supported](https://github.com/Louisvdw/dbus-serialbattery/wiki/BMS-types-supported) + * [FAQ](https://github.com/Louisvdw/dbus-serialbattery/wiki/FAQ) + * [Features](https://github.com/Louisvdw/dbus-serialbattery/wiki/Features) + * [How to install](https://github.com/Louisvdw/dbus-serialbattery/wiki/How-to-install) + * [Troubleshoot](https://github.com/Louisvdw/dbus-serialbattery/wiki/Troubleshoot) -## Supporting this project: +### Supporting this project: If you find this driver helpful please considder supporting this project. You can buy me a Ko-Fi or get in contact if you would like to donate hardware. [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z73LCW1) or using [Paypal.me](https://paypal.me/innernet) -## Developer Remarks +### Developer Remarks To develop this project, install the requirements. This project makes use of velib_python which is pre-installed on -Venus OS Devices under `/opt/victronenergy/dbus-systemcalc-py/ext/velib_python`. To use the python files locally, +Venus-OS Devices under `/opt/victronenergy/dbus-systemcalc-py/ext/velib_python`. To use the python files locally, `git clone` the [velib_python](https://github.com/victronenergy/velib_python) project to velib_python and add velib_python to the `PYTHONPATH` environment variable. -## How it works +#### How it works * Each supported BMS needs to implement the abstract base class `Battery` from `battery.py`. * `dbus-serialbattery.py` tries to figure out the correct connected BMS by looping through all known implementations of `Battery` and executing its `test_connection()`. If this returns true, `dbus-serialbattery.py` sticks with this battery -and then periodically executes `dbushelper.publish_battery()`. `publish_battery()` executes `Battery.refresh_data()` which +and then periodically executes `dbushelpert.publish_battery()`. `publish_battery()` executes `Battery.refresh_data()` which updates the fields of Battery. It then publishes those fields to dbus using `dbushelper.publish_dbus()` * The Victron Device will be "controlled" by the values published on `/Info/` - namely: * `/Info/MaxChargeCurrent ` * `/Info/MaxDischargeCurrent` * `/Info/MaxChargeVoltage` - * `/Info/BatteryLowVoltage` (note that Low Voltage is ignored by the system) + * `/Info/BatteryLowVoltage` * `/Info/ChargeRequest` (not implemented in dbus-serialbattery) -For more details on the Victron dbus interface see [the official Victron dbus documentation](https://github.com/victronenergy/venus/wiki/dbus). +For more details on the victron dbus interface see [the official victron dbus documentation](https://github.com/victronenergy/venus/wiki/dbus) + +## Screenshots + +### Venus OS + +![VenusOS](docs/screenshots/venus-os_001.png) +![VenusOS](docs/screenshots/venus-os_002.png) +![VenusOS](docs/screenshots/venus-os_003.png) +![VenusOS](docs/screenshots/venus-os_004.png) +![VenusOS](docs/screenshots/venus-os_005.png) +![VenusOS](docs/screenshots/venus-os_006.png) +![VenusOS](docs/screenshots/venus-os_007.png) +![VenusOS](docs/screenshots/venus-os_008.png) +![VenusOS](docs/screenshots/venus-os_009.png) +![VenusOS](docs/screenshots/venus-os_010.png) +![VenusOS](docs/screenshots/venus-os_011.png) +![VenusOS](docs/screenshots/venus-os_012.png) +![VenusOS](docs/screenshots/venus-os_013.png) + +### VRM Portal + +![VenusOS](docs/screenshots/vrm-portal_001.png) +![VenusOS](docs/screenshots/vrm-portal_002.png) +![VenusOS](docs/screenshots/vrm-portal_003.png) +![VenusOS](docs/screenshots/vrm-portal_004.png) +![VenusOS](docs/screenshots/vrm-portal_005.png) +![VenusOS](docs/screenshots/vrm-portal_006.png) +![VenusOS](docs/screenshots/vrm-portal_007.png) +![VenusOS](docs/screenshots/vrm-portal_008.png) +![VenusOS](docs/screenshots/vrm-portal_009.png) diff --git a/etc/dbus-serialbattery/bms/hlpdatabms4s.py b/etc/dbus-serialbattery/bms/hlpdatabms4s.py index 3f99aa8e..d4a350b1 100644 --- a/etc/dbus-serialbattery/bms/hlpdatabms4s.py +++ b/etc/dbus-serialbattery/bms/hlpdatabms4s.py @@ -202,7 +202,7 @@ def read_serial_data2(command, port, baud, time, min_len): try: with serial.Serial(port, baudrate=baud, timeout=0.5) as ser: ret = read_serialport_data2(ser, command, time, min_len) - if False is not ret: + if ret is True: return ret return False diff --git a/etc/dbus-serialbattery/bms/seplos.py b/etc/dbus-serialbattery/bms/seplos.py index 1adf12bf..c9519fd0 100644 --- a/etc/dbus-serialbattery/bms/seplos.py +++ b/etc/dbus-serialbattery/bms/seplos.py @@ -5,11 +5,6 @@ import serial -def int_from_hex_ascii(to_decode, signed=False): - return int.from_bytes( - bytes.fromhex(to_decode.decode("ascii")), byteorder="big", signed=signed - ) - class Seplos(Battery): def __init__(self, port, baud, address=0x00): @@ -93,14 +88,14 @@ def test_connection(self): return False def get_settings(self): - # After successful connection get_settings will be call to set up the battery. + # After successful connection get_settings will be called to set up the battery. # Set the current limits, populate cell count, etc. # Return True if success, False for failure - # Uncomment if BMS does not supply capacity - # self.capacity = BATTERY_CAPACITY + # BMS does not provide max charge-/discharge, so we have to use hardcoded/config values self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count From 335a0e7f85d3c8cdcd97ca5ebf26f81aaf5f08d7 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 23:00:51 +0200 Subject: [PATCH 159/209] GitHub pages config change --- .github/workflows/github-pages.yml | 3 +++ etc/dbus-serialbattery/bms/seplos.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 158b8843..ef3c00ab 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -39,6 +39,9 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest + # execute only in Louisvdw repository + if: github.repository_owner == 'Louisvdw' + #defaults: # run: # working-directory: 'docs' # Here the path to the folder where package-lock.json is located. diff --git a/etc/dbus-serialbattery/bms/seplos.py b/etc/dbus-serialbattery/bms/seplos.py index c9519fd0..083d6bc4 100644 --- a/etc/dbus-serialbattery/bms/seplos.py +++ b/etc/dbus-serialbattery/bms/seplos.py @@ -5,7 +5,6 @@ import serial - class Seplos(Battery): def __init__(self, port, baud, address=0x00): super(Seplos, self).__init__(port, baud, address) From 9f1da97338008ba7de8cbad40d348996ef7c3ff8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 2 May 2023 23:07:53 +0200 Subject: [PATCH 160/209] cleanup --- CHANGELOG.md | 2 +- etc/dbus-serialbattery/disabledriver.sh | 13 +++++++++---- etc/dbus-serialbattery/reinstalllocal.sh | 17 ++++++----------- etc/dbus-serialbattery/utils.py | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24edc9ce..c4438d07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,7 @@ * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel * Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek * Changed: Logging to get relevant data by @mr-manuel -* Changed: Moved Bluetooth part to `installble.sh` by @mr-manuel +* Changed: Moved Bluetooth part to `reinstalllocal.sh` by @mr-manuel * Changed: Moved BMS scripts to subfolder by @mr-manuel * Changed: Optimized installation scripts by @mr-manuel * Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disabledriver.sh index 33aa677f..e55fd246 100755 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disabledriver.sh @@ -12,13 +12,18 @@ sh /opt/victronenergy/swupdate-scripts/remount-rw.sh rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf rm -rf /service/dbus-blebattery.* -# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 -# can be removed on second release (>1.0.0) -rm -rf /service/dbus-blebattery-* - # kill if running pkill -f "python .*/$DRIVERNAME.py" # remove install-script from rc.local sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local + + + +### temporary +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +# can be removed on second release (>1.0.0) +rm -rf /service/dbus-blebattery-* + +# can be removed on second release (>1.0.0) sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstalllocal.sh index 19b804a6..1a59ff9e 100755 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstalllocal.sh @@ -96,20 +96,15 @@ if [ $length -gt 0 ]; then # setup cronjob to restart Bluetooth grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root - # add install-script to rc.local to be ready for firmware update - filename=/data/rc.local - if [ ! -f $filename ]; then - echo "#!/bin/bash" >> $filename - chmod 755 $filename - fi - grep -qxF "sh /data/etc/dbus-serialbattery/installble.sh" $filename || echo "sh /data/etc/dbus-serialbattery/installble.sh" >> $filename - # kill if running, needed when an adapter changes - pkill -f "python .*/dbus-serialbattery.py" - - # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 + ### temporary | start # can be removed on second release (>1.0.0) + # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 rm -rf /service/dbus-blebattery-* + # remove old entry from rc.local + sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local + ### temporary | end + # remove existing driver to cleanup rm -rf /service/dbus-blebattery.* diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 4037fbbf..0d94e36b 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230502)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230503)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 74b459abd9d5ac031cd68550cff2bc6fed9c932f Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 08:51:14 +0200 Subject: [PATCH 161/209] Renamed scripts for better reading #532 --- .github/workflows/release.yml | 1 - CHANGELOG.md | 11 ++-- docs/docs/general/install.md | 14 ++--- .../{disabledriver.sh => disable.sh} | 18 +++--- etc/dbus-serialbattery/install-local.sh | 23 ++++++++ etc/dbus-serialbattery/install-nightly.sh | 2 +- .../{installqml.sh => install-qml.sh} | 0 .../{installrelease.sh => install-release.sh} | 2 +- etc/dbus-serialbattery/installlocal.sh | 10 ---- .../{reinstalllocal.sh => reinstall-local.sh} | 55 +++++++++++-------- .../restart-driver-and-ble.sh | 10 ---- etc/dbus-serialbattery/restart-driver.sh | 27 +++++++++ etc/dbus-serialbattery/restartservice.sh | 6 -- .../{restoregui.sh => restore-gui.sh} | 0 etc/dbus-serialbattery/uninstall.sh | 24 +++++--- 15 files changed, 122 insertions(+), 81 deletions(-) rename etc/dbus-serialbattery/{disabledriver.sh => disable.sh} (53%) create mode 100755 etc/dbus-serialbattery/install-local.sh rename etc/dbus-serialbattery/{installqml.sh => install-qml.sh} (100%) rename etc/dbus-serialbattery/{installrelease.sh => install-release.sh} (85%) delete mode 100755 etc/dbus-serialbattery/installlocal.sh rename etc/dbus-serialbattery/{reinstalllocal.sh => reinstall-local.sh} (80%) delete mode 100755 etc/dbus-serialbattery/restart-driver-and-ble.sh create mode 100755 etc/dbus-serialbattery/restart-driver.sh delete mode 100755 etc/dbus-serialbattery/restartservice.sh rename etc/dbus-serialbattery/{restoregui.sh => restore-gui.sh} (100%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c3fe7829..672ecf61 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,6 @@ jobs: --exclude bms/battery_template.py \ --exclude bms/revov.py \ --exclude bms/test_max17853.py \ - --exclude restartservice.sh \ etc/dbus-serialbattery/ - name: Release diff --git a/CHANGELOG.md b/CHANGELOG.md index c4438d07..5797935f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,8 +36,8 @@ * Added: Support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 by @peterohman * Added: Support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 by @wollew * Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) by @mr-manuel -* Changed: `reinstalllocal.sh` to recreate `/data/conf/serial-starter.d` if deleted by `disabledriver.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository by @mr-manuel -* Changed: Added QML to `restoregui.sh` by @mr-manuel +* Changed: `reinstall-local.sh` to recreate `/data/conf/serial-starter.d`, if deleted by `disable.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository by @mr-manuel +* Changed: Added QML to `restore-gui.sh` by @mr-manuel * Changed: Bash output by @mr-manuel * Changed: Default config file by @mr-manuel * Added missing descriptions to make it much clearer to understand by @mr-manuel @@ -57,13 +57,14 @@ * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel * Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek * Changed: Logging to get relevant data by @mr-manuel -* Changed: Moved Bluetooth part to `reinstalllocal.sh` by @mr-manuel +* Changed: Moved Bluetooth part to `reinstall-local.sh` by @mr-manuel * Changed: Moved BMS scripts to subfolder by @mr-manuel -* Changed: Optimized installation scripts by @mr-manuel * Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel * Changed: Removed wildcard imports from several BMS drivers and fixed black lint errors by @mr-manuel -* Changed: Serial-Starter file is now created from `reinstalllocal.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 by @mr-manuel +* Changed: Renamed scripts for better reading #532 by @mr-manuel +* Changed: Reworked and optimized installation scripts by @mr-manuel * Changed: Separate Time-To-Go and Time-To-SoC activation by @mr-manuel +* Changed: Serial-Starter file is now created from `reinstall-local.sh`. Fixes also https://github.com/Louisvdw/dbus-serialbattery/issues/520 by @mr-manuel * Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS by @mr-manuel * Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` by @mr-manuel * Changed: Time-To-Soc string from `days, HR:MN:SC` to `d h m s` (same as Time-To-Go) by @mr-manuel diff --git a/docs/docs/general/install.md b/docs/docs/general/install.md index 28e13577..c4b8ad45 100644 --- a/docs/docs/general/install.md +++ b/docs/docs/general/install.md @@ -74,9 +74,9 @@ In [VRM](https://vrm.victronenergy.com/) look under the device list for your ins 2. Run these commands to install or update to the latest release version. ```bash - wget -O /tmp/installrelease.sh https://raw.githubusercontent.com/Louisvdw/dbus-serialbattery/master/etc/dbus-serialbattery/installrelease.sh + wget -O /tmp/install-release.sh https://raw.githubusercontent.com/Louisvdw/dbus-serialbattery/master/etc/dbus-serialbattery/install-release.sh - bash /tmp/installrelease.sh + bash /tmp/install-release.sh reboot ``` @@ -138,7 +138,7 @@ If you use the cell voltage limits, temperature limits or SoC limits you also ne ### Settings location/path -💡 After updating the settings reboot the device or run `/data/etc/dbus-serialbattery/reinstalllocal.sh` to apply the changes. +💡 After updating the settings reboot the device or run `/data/etc/dbus-serialbattery/reinstall-local.sh` to apply the changes. #### Driver version `<= v0.14.3` Edit `/data/etc/dbus-serialbattery/utils.py` to update the constants. @@ -204,7 +204,7 @@ Edit `/data/etc/dbus-serialbattery/dbus-serialbattery.py` and uncommented (witho You can disable the driver so that it will not be run by the GX device. To do that run the following command in SSH. ```bash -bash /data/etc/dbus-serialbattery/disabledriver.sh +bash /data/etc/dbus-serialbattery/disable.sh ``` You also need to configure your MPPTs to run in `Stand alone mode` again. Follow the Victron guide for [Err 67 - BMS Connection lost](https://www.victronenergy.com/live/mppt-error-codes#err_67_-_bms_connection_lost). @@ -213,7 +213,7 @@ You also need to configure your MPPTs to run in `Stand alone mode` again. Follow To enable the driver again you can run the installer. ```bash -bash /data/etc/dbus-serialbattery/reinstalllocal.sh +bash /data/etc/dbus-serialbattery/reinstall-local.sh ``` ## Uninstall/remove the driver @@ -236,11 +236,11 @@ rm -rf /opt/victronenergy/service/dbus-serialbattery rm -rf /opt/victronenergy/service-templates/dbus-serialbattery rm -rf /opt/victronenergy/dbus-serialbattery -# kill if running +# kill driver, if running pkill -f "python .*/dbus-serialbattery.py" # remove install-script from rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local ``` > If after the uninstall for some reason several items in the GUI were red, DO NOT reboot your GX device. See [Uninstalling driver bricked my cerbo #576](https://github.com/Louisvdw/dbus-serialbattery/issues/576) diff --git a/etc/dbus-serialbattery/disabledriver.sh b/etc/dbus-serialbattery/disable.sh similarity index 53% rename from etc/dbus-serialbattery/disabledriver.sh rename to etc/dbus-serialbattery/disable.sh index e55fd246..697f42df 100755 --- a/etc/dbus-serialbattery/disabledriver.sh +++ b/etc/dbus-serialbattery/disable.sh @@ -12,18 +12,18 @@ sh /opt/victronenergy/swupdate-scripts/remount-rw.sh rm -f /data/conf/serial-starter.d/$DRIVERNAME.conf rm -rf /service/dbus-blebattery.* -# kill if running +# kill driver, if running. It gets restarted by the service daemon pkill -f "python .*/$DRIVERNAME.py" -# remove install-script from rc.local -sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local - +# remove install script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstall-local.sh/d" /data/rc.local -### temporary +### needed for upgrading from older versions | start ### # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 -# can be removed on second release (>1.0.0) rm -rf /service/dbus-blebattery-* - -# can be removed on second release (>1.0.0) -sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local +# remove old install script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +# remove old entry from rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local +### needed for upgrading from older versions | end ### diff --git a/etc/dbus-serialbattery/install-local.sh b/etc/dbus-serialbattery/install-local.sh new file mode 100755 index 00000000..61ffccf1 --- /dev/null +++ b/etc/dbus-serialbattery/install-local.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# remove comment for easier troubleshooting +#set -x + +# backup config.ini +if [ -f "/data/etc/dbus-serialbattery/config.ini" ]; then + mv /data/etc/dbus-serialbattery/config.ini /data/etc/config.ini +fi + +# remove old driver +rm -rf /data/etc/dbus-serialbattery + +# extract driver +tar -zxf ./venus-data.tar.gz -C /data + +# restore config.ini +if [ -f "/data/etc/config.ini" ]; then + mv /data/etc/config.ini /data/etc/dbus-serialbattery/config.ini +fi + +# install driver +sh /data/etc/dbus-serialbattery/reinstall-local.sh diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh index f60954c1..061699b8 100755 --- a/etc/dbus-serialbattery/install-nightly.sh +++ b/etc/dbus-serialbattery/install-nightly.sh @@ -62,4 +62,4 @@ chmod +x /data/etc/dbus-serialbattery/service/run chmod +x /data/etc/dbus-serialbattery/service/log/run # run install script -bash /data/etc/dbus-serialbattery/reinstalllocal.sh +bash /data/etc/dbus-serialbattery/reinstall-local.sh diff --git a/etc/dbus-serialbattery/installqml.sh b/etc/dbus-serialbattery/install-qml.sh similarity index 100% rename from etc/dbus-serialbattery/installqml.sh rename to etc/dbus-serialbattery/install-qml.sh diff --git a/etc/dbus-serialbattery/installrelease.sh b/etc/dbus-serialbattery/install-release.sh similarity index 85% rename from etc/dbus-serialbattery/installrelease.sh rename to etc/dbus-serialbattery/install-release.sh index 1b5cb6ee..f95b2c92 100755 --- a/etc/dbus-serialbattery/installrelease.sh +++ b/etc/dbus-serialbattery/install-release.sh @@ -7,4 +7,4 @@ curl -s https://api.github.com/repos/Louisvdw/dbus-serialbattery/releases/latest | grep "browser_download_url.*gz" | cut -d : -f 2,3 | tr -d \" | wget -O venus-data.tar.gz -qi - # extract and install driver -sh /data/etc/dbus-serialbattery/installlocal.sh +sh /data/etc/dbus-serialbattery/install-local.sh diff --git a/etc/dbus-serialbattery/installlocal.sh b/etc/dbus-serialbattery/installlocal.sh deleted file mode 100755 index a4cd70c1..00000000 --- a/etc/dbus-serialbattery/installlocal.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# remove comment for easier troubleshooting -#set -x - -# extract driver -tar -zxf ./venus-data.tar.gz -C /data - -# install driver -sh /data/etc/dbus-serialbattery/reinstalllocal.sh diff --git a/etc/dbus-serialbattery/reinstalllocal.sh b/etc/dbus-serialbattery/reinstall-local.sh similarity index 80% rename from etc/dbus-serialbattery/reinstalllocal.sh rename to etc/dbus-serialbattery/reinstall-local.sh index 1a59ff9e..f29fd4c3 100755 --- a/etc/dbus-serialbattery/reinstalllocal.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -17,13 +17,13 @@ mkdir /opt/victronenergy/$DRIVERNAME/bms cp -f /data/etc/$DRIVERNAME/* /opt/victronenergy/$DRIVERNAME &>/dev/null cp -f /data/etc/$DRIVERNAME/bms/* /opt/victronenergy/$DRIVERNAME/bms &>/dev/null cp -rf /data/etc/$DRIVERNAME/service /opt/victronenergy/service-templates/$DRIVERNAME -sh /data/etc/$DRIVERNAME/installqml.sh +sh /data/etc/$DRIVERNAME/install-qml.sh # check if serial-starter.d was deleted serialstarter_path="/data/conf/serial-starter.d" serialstarter_file="$serialstarter_path/dbus-serialbattery.conf" -# check if folder is a file (older versions of this driver) +# check if folder is a file (older versions of this driver < v1.0.0) if [ -f $serialstarter_path ]; then rm -f $serialstarter_path fi @@ -40,16 +40,13 @@ if [ ! -f $serialstarter_file ]; then echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" >> $serialstarter_file fi -# restart if running -pkill -f "python .*/$DRIVERNAME.py" - # add install-script to rc.local to be ready for firmware update filename=/data/rc.local if [ ! -f $filename ]; then echo "#!/bin/bash" >> $filename chmod 755 $filename fi -grep -qxF "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" $filename || echo "sh /data/etc/$DRIVERNAME/reinstalllocal.sh" >> $filename +grep -qxF "sh /data/etc/$DRIVERNAME/reinstall-local.sh" $filename || echo "sh /data/etc/$DRIVERNAME/reinstall-local.sh" >> $filename # add empty config.ini, if it does not exist to make it easier for users to add custom settings filename=/data/etc/$DRIVERNAME/config.ini @@ -58,6 +55,7 @@ if [ ! -f $filename ]; then echo "" >> $filename echo "; If you want to add custom settings, then check the settings you want to change in \"config.default.ini\"" >> $filename echo "; and add them below to persist future driver updates." >> $filename + echo "" >> $filename fi @@ -80,6 +78,8 @@ IFS="," read -r -a bms_array <<< "$bluetooth_bms_clean" length=${#bms_array[@]} # echo $length +# always remove existing blebattery services to cleanup +rm -rf /service/dbus-blebattery.* if [ $length -gt 0 ]; then @@ -96,19 +96,6 @@ if [ $length -gt 0 ]; then # setup cronjob to restart Bluetooth grep -qxF "5 0,12 * * * /etc/init.d/bluetooth restart" /var/spool/cron/root || echo "5 0,12 * * * /etc/init.d/bluetooth restart" >> /var/spool/cron/root - - ### temporary | start - # can be removed on second release (>1.0.0) - # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 - rm -rf /service/dbus-blebattery-* - # remove old entry from rc.local - sed -i "/sh \/data\/etc\/$DRIVERNAME\/installble.sh/d" /data/rc.local - ### temporary | end - - - # remove existing driver to cleanup - rm -rf /service/dbus-blebattery.* - # function to install ble battery install_blebattery_service() { mkdir -p /service/dbus-blebattery.$1/log @@ -126,8 +113,8 @@ if [ $length -gt 0 ]; then echo "Packages installed." echo "" - # install_blebattery_service 0 Jkbms_Ble C8:47:8C:12:34:56 - # install_blebattery_service 1 Jkbms_Ble C8:47:8C:78:9A:BC + # install_blebattery_service 0 Jkbms_Ble C8:47:8C:00:00:00 + # install_blebattery_service 1 Jkbms_Ble C8:47:8C:00:00:11 for (( i=0; i<${length}; i++ )); do @@ -136,12 +123,36 @@ if [ $length -gt 0 ]; then done else + + # remove cronjob + sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root + echo "No Bluetooth battery configuration found in \"/data/etc/dbus-serialbattery/config.ini\"." echo "You can ignore this, if you are using only a serial connection." + fi ### BLUETOOTH PART | END ### +### needed for upgrading from older versions | start ### +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +rm -rf /service/dbus-blebattery-* +# remove old install script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +# remove old entry from rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local +### needed for upgrading from older versions | end ### + + +# kill driver, if running. It gets restarted by the service daemon +pkill -f "python .*/$DRIVERNAME.py" + +# restart bluetooth service, if Bluetooth BMS configured +if [ $length -gt 0 ]; then + /etc/init.d/bluetooth restart +fi + + # install notes echo echo @@ -156,7 +167,7 @@ echo " If your Bluetooth BMS are nearby you can show the MAC address with echo echo " 2. Make sure to disable Settings -> Bluetooth in the remote console/GUI to prevent reconnects every minute." echo -echo " 3. Re-run \"/data/etc/dbus-serialbattery/reinstalllocal.sh\", if the Bluetooth BMS were not added to the \"config.ini\" before." +echo " 3. Re-run \"/data/etc/dbus-serialbattery/reinstall-local.sh\", if the Bluetooth BMS were not added to the \"config.ini\" before." echo echo " ATTENTION!" echo " If you changed the default connection PIN of your BMS, then you have to pair the BMS first using OS tools like the \"bluetoothctl\"." diff --git a/etc/dbus-serialbattery/restart-driver-and-ble.sh b/etc/dbus-serialbattery/restart-driver-and-ble.sh deleted file mode 100755 index d85d1c3d..00000000 --- a/etc/dbus-serialbattery/restart-driver-and-ble.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# remove comment for easier troubleshooting -#set -x - -sh /data/etc/dbus-serialbattery/reinstalllocal.sh - -sh /data/etc/dbus-serialbattery/restartservice.sh - -/etc/init.d/bluetooth restart diff --git a/etc/dbus-serialbattery/restart-driver.sh b/etc/dbus-serialbattery/restart-driver.sh new file mode 100755 index 00000000..59b0b5cd --- /dev/null +++ b/etc/dbus-serialbattery/restart-driver.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# remove comment for easier troubleshooting +#set -x + +# copy config.ini in case it was changed +cp -f /data/etc/dbus-serialbattery/config.ini /opt/victronenergy/dbus-serialbattery/config.ini + +# would not restart ble services +# svc -d -u /service/dbus-serialbattery + +# kill driver, if running. It gets restarted by the service daemon +pkill -f "python .*/$DRIVERNAME.py" + + +# get BMS list from config file +bluetooth_bms=$(awk -F "=" '/BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) +# clear whitespaces +bluetooth_bms_clean="$(echo $bluetooth_bms | sed 's/\s*,\s*/,/g')" +# split into array +IFS="," read -r -a bms_array <<< "$bluetooth_bms_clean" +length=${#bms_array[@]} + +# restart bluetooth service, if Bluetooth BMS configured +if [ $length -gt 0 ]; then + /etc/init.d/bluetooth restart +fi diff --git a/etc/dbus-serialbattery/restartservice.sh b/etc/dbus-serialbattery/restartservice.sh deleted file mode 100755 index 8edcb59a..00000000 --- a/etc/dbus-serialbattery/restartservice.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# remove comment for easier troubleshooting -#set -x - -svc -d -u /service/dbus-serialbattery diff --git a/etc/dbus-serialbattery/restoregui.sh b/etc/dbus-serialbattery/restore-gui.sh similarity index 100% rename from etc/dbus-serialbattery/restoregui.sh rename to etc/dbus-serialbattery/restore-gui.sh diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index 5587acfc..da024a4d 100755 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -13,20 +13,26 @@ rm -rf /opt/victronenergy/service-templates/dbus-serialbattery rm -rf /opt/victronenergy/dbus-serialbattery rm -rf /service/dbus-blebattery.* -# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 -# can be removed on second release (>1.0.0) -rm -rf /service/dbus-blebattery-* - -# kill if running -pkill -f "python .*/dbus-serialbattery.py" - # remove install-script from rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local # remove cronjob sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root + +### needed for upgrading from older versions | start ### +# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 +rm -rf /service/dbus-blebattery-* +# remove old install script from rc.local +sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +# remove old entry from rc.local +sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local +### needed for upgrading from older versions | end ### + + +# kill driver, if running +pkill -f "python .*/dbus-serialbattery.py" + # uninstall modules read -r -p "Do you also want to uninstall bleak, python3-pip and python3-modules? If you don't know select y. [Y/n] " response echo From dff74d73caa52627321b11f3951e3ebf4ea04150 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 08:58:04 +0200 Subject: [PATCH 162/209] update docusaurus dependencies --- docs/yarn.lock | 820 +++++++++++++++++++++++++------------------------ 1 file changed, 418 insertions(+), 402 deletions(-) diff --git a/docs/yarn.lock b/docs/yarn.lock index 7a9d50f1..00c2befe 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2,24 +2,24 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz#85ff36b2673654a393c8c505345eaedd6eaa4f70" - integrity sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg== +"@algolia/autocomplete-core@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.8.2.tgz#8d758c8652742e2761450d2b615a841fca24e10e" + integrity sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ== dependencies: - "@algolia/autocomplete-shared" "1.7.4" + "@algolia/autocomplete-shared" "1.8.2" -"@algolia/autocomplete-preset-algolia@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz#610ee1d887962f230b987cba2fd6556478000bc3" - integrity sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ== +"@algolia/autocomplete-preset-algolia@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.8.2.tgz#706e87f94c5f198c0e90502b97af09adeeddcc79" + integrity sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA== dependencies: - "@algolia/autocomplete-shared" "1.7.4" + "@algolia/autocomplete-shared" "1.8.2" -"@algolia/autocomplete-shared@1.7.4": - version "1.7.4" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz#78aea1140a50c4d193e1f06a13b7f12c5e2cbeea" - integrity sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg== +"@algolia/autocomplete-shared@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.8.2.tgz#e6972df5c6935a241f16e4909aa82902338e029d" + integrity sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g== "@algolia/cache-browser-local-storage@4.17.0": version "4.17.0" @@ -145,10 +145,10 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" - integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": + version "7.21.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" + integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== "@babel/core@7.12.9": version "7.12.9" @@ -173,32 +173,32 @@ source-map "^0.5.0" "@babel/core@^7.18.6", "@babel/core@^7.19.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" + integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helpers" "^7.21.5" + "@babel/parser" "^7.21.8" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" - integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== +"@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" + integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.21.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -211,45 +211,46 @@ "@babel/types" "^7.18.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz#817f73b6c59726ab39f6ba18c234268a519e5abb" + integrity sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g== dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/types" "^7.21.5" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" - integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" + integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== dependencies: - "@babel/compat-data" "^7.21.4" + "@babel/compat-data" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" - integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz#205b26330258625ef8869672ebca1e0dee5a0f02" + integrity sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-environment-visitor" "^7.21.5" "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.5" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-replace-supers" "^7.21.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" - integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz#a7886f61c2e29e21fd4aaeaf1e473deba6b571dc" + integrity sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.3.1" + semver "^6.3.0" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -263,17 +264,10 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" + integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": version "7.21.0" @@ -290,12 +284,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" - integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== +"@babel/helper-member-expression-to-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz#3b1a009af932e586af77c1030fba9ee0bde396c0" + integrity sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg== dependencies: - "@babel/types" "^7.21.0" + "@babel/types" "^7.21.5" "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": version "7.21.4" @@ -304,19 +298,19 @@ dependencies: "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" + integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -330,10 +324,10 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" @@ -345,24 +339,24 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz#a6ad005ba1c7d9bc2973dfde05a1bba7065dde3c" + integrity sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-member-expression-to-functions" "^7.21.5" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.21.5" "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" @@ -378,10 +372,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" @@ -403,14 +397,14 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" + integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" "@babel/highlight@^7.18.6": version "7.18.6" @@ -421,10 +415,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" - integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" + integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -622,6 +616,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -636,7 +637,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4": +"@babel/plugin-syntax-jsx@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== @@ -706,12 +707,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-arrow-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== +"@babel/plugin-transform-arrow-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" + integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-async-to-generator@^7.20.7": version "7.20.7" @@ -751,12 +752,12 @@ "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== +"@babel/plugin-transform-computed-properties@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" + integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.21.3": @@ -789,12 +790,12 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" - integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== +"@babel/plugin-transform-for-of@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" + integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-function-name@^7.18.9": version "7.18.9" @@ -827,14 +828,14 @@ "@babel/helper-module-transforms" "^7.20.11" "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-modules-commonjs@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" - integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" "@babel/plugin-transform-modules-systemjs@^7.20.11": version "7.20.11" @@ -913,15 +914,15 @@ "@babel/plugin-transform-react-jsx" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" - integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz#bd98f3b429688243e4fa131fe1cbb2ef31ce6f38" + integrity sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.21.0" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/types" "^7.21.5" "@babel/plugin-transform-react-pure-annotations@^7.18.6": version "7.18.6" @@ -931,12 +932,12 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-regenerator@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== +"@babel/plugin-transform-regenerator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" + integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.18.6": @@ -1004,12 +1005,12 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-unicode-escapes@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" + integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -1020,13 +1021,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.18.6", "@babel/preset-env@^7.19.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.4.tgz#a952482e634a8dd8271a3fe5459a16eb10739c58" - integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" + integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== dependencies: - "@babel/compat-data" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/compat-data" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" @@ -1051,6 +1052,7 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1060,22 +1062,22 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.20.7" + "@babel/plugin-transform-arrow-functions" "^7.21.5" "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" "@babel/plugin-transform-block-scoping" "^7.21.0" "@babel/plugin-transform-classes" "^7.21.0" - "@babel/plugin-transform-computed-properties" "^7.20.7" + "@babel/plugin-transform-computed-properties" "^7.21.5" "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.21.0" + "@babel/plugin-transform-for-of" "^7.21.5" "@babel/plugin-transform-function-name" "^7.18.9" "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" "@babel/plugin-transform-modules-amd" "^7.20.11" - "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" "@babel/plugin-transform-modules-systemjs" "^7.20.11" "@babel/plugin-transform-modules-umd" "^7.18.6" "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" @@ -1083,17 +1085,17 @@ "@babel/plugin-transform-object-super" "^7.18.6" "@babel/plugin-transform-parameters" "^7.21.3" "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-regenerator" "^7.21.5" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" "@babel/plugin-transform-spread" "^7.20.7" "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.21.4" + "@babel/types" "^7.21.5" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -1124,14 +1126,14 @@ "@babel/plugin-transform-react-pure-annotations" "^7.18.6" "@babel/preset-typescript@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" - integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz#68292c884b0e26070b4d66b202072d391358395f" + integrity sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-syntax-jsx" "^7.21.4" - "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" "@babel/plugin-transform-typescript" "^7.21.3" "@babel/regjsgen@^0.8.0": @@ -1140,17 +1142,17 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime-corejs3@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz#6e4939d9d9789ff63e2dc58e88f13a3913a24eba" - integrity sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.21.5.tgz#a6d4e132ab1cb2fae2354f02284ebb6e07b4f7d8" + integrity sha512-FRqFlFKNazWYykft5zvzuEl1YyTDGsIRrjV9rvxvYkUC7W/ueBng1X68Xd6uRMzAaJ0xMKn08/wem5YS1lpX8w== dependencies: core-js-pure "^3.25.1" regenerator-runtime "^0.13.11" "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.8.4": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" + integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== dependencies: regenerator-runtime "^0.13.11" @@ -1163,28 +1165,28 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== dependencies: "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.4.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== +"@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.4.4": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== dependencies: - "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-string-parser" "^7.21.5" "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" @@ -1198,19 +1200,19 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@docsearch/css@3.3.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.3.tgz#f9346c9e24602218341f51b8ba91eb9109add434" - integrity sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg== +"@docsearch/css@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.4.tgz#533719eac0aa3934318074e7e981e633727ad2fd" + integrity sha512-vDwCDoVXDgopw/hvr0zEADew2wWaGP8Qq0Bxhgii1Ewz2t4fQeyJwIRN/mWADeLFYPVkpz8TpEbxya/i6Tm0WA== "@docsearch/react@^3.1.1": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.3.tgz#907b6936a565f880b4c0892624b4f7a9f132d298" - integrity sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q== + version "3.3.4" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.4.tgz#d49cf9e5d939145c9fe688113c5bdf41975d8ae7" + integrity sha512-aeOf1WC5zMzBEi2SI6WWznOmIo9rnpN4p7a3zHXxowVciqlI4HsZGtOR9nFOufLeolv7HibwLlaM0oyUqJxasw== dependencies: - "@algolia/autocomplete-core" "1.7.4" - "@algolia/autocomplete-preset-algolia" "1.7.4" - "@docsearch/css" "3.3.3" + "@algolia/autocomplete-core" "1.8.2" + "@algolia/autocomplete-preset-algolia" "1.8.2" + "@docsearch/css" "3.3.4" algoliasearch "^4.0.0" "@docusaurus/core@2.4.0": @@ -1941,9 +1943,9 @@ "@types/node" "*" "@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#9fd20b3974bdc2bcd4ac6567e2e0f6885cb2cf41" + integrity sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" @@ -1972,18 +1974,19 @@ "@types/json-schema" "*" "@types/estree@*", "@types/estree@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.33" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" - integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + version "4.17.34" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.34.tgz#c119e85b75215178bc127de588e93100698ab4cc" + integrity sha512-fvr49XlCGoUj2Pp730AItckfjat4WNb0lb3kfrLWffd+RLeoGAMsq7UOy04PAPtoL01uKwcp6u8nhzpgpDYr3w== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" + "@types/send" "*" "@types/express@*", "@types/express@^4.17.13": version "4.17.17" @@ -2013,9 +2016,9 @@ integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== "@types/http-proxy@^1.17.8": - version "1.17.10" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.10.tgz#e576c8e4a0cc5c6a138819025a88e167ebb38d6c" - integrity sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g== + version "1.17.11" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.11.tgz#0ca21949a5588d55ac2b659b69035c84bd5da293" + integrity sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA== dependencies: "@types/node" "*" @@ -2055,10 +2058,15 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/node@*": - version "18.15.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + version "18.16.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" + integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== "@types/node@^17.0.5": version "17.0.45" @@ -2117,9 +2125,9 @@ "@types/react" "*" "@types/react@*": - version "18.0.35" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741" - integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag== + version "18.2.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21" + integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2142,6 +2150,14 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -2188,125 +2204,125 @@ dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== +"@webassemblyjs/ast@1.11.5", "@webassemblyjs/ast@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.5.tgz#6e818036b94548c1fb53b754b5cae3c9b208281c" + integrity sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ== dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-numbers" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== +"@webassemblyjs/floating-point-hex-parser@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz#e85dfdb01cad16b812ff166b96806c050555f1b4" + integrity sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ== -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== +"@webassemblyjs/helper-api-error@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz#1e82fa7958c681ddcf4eabef756ce09d49d442d1" + integrity sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA== -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== +"@webassemblyjs/helper-buffer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz#91381652ea95bb38bbfd270702351c0c89d69fba" + integrity sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg== -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== +"@webassemblyjs/helper-numbers@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz#23380c910d56764957292839006fecbe05e135a9" + integrity sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA== dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/floating-point-hex-parser" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz#e258a25251bc69a52ef817da3001863cc1c24b9f" + integrity sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz#966e855a6fae04d5570ad4ec87fbcf29b42ba78e" + integrity sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz#b2db1b33ce9c91e34236194c2b5cba9b25ca9d60" + integrity sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.5.tgz#482e44d26b6b949edf042a8525a66c649e38935a" + integrity sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.5.tgz#83bef94856e399f3740e8df9f63bc47a987eae1a" + integrity sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz#93ee10a08037657e21c70de31c47fdad6b522b2d" + integrity sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/helper-wasm-section" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-opt" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + "@webassemblyjs/wast-printer" "1.11.5" + +"@webassemblyjs/wasm-gen@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz#ceb1c82b40bf0cf67a492c53381916756ef7f0b1" + integrity sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wasm-opt@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz#b52bac29681fa62487e16d3bb7f0633d5e62ca0a" + integrity sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-buffer" "1.11.5" + "@webassemblyjs/wasm-gen" "1.11.5" + "@webassemblyjs/wasm-parser" "1.11.5" + +"@webassemblyjs/wasm-parser@1.11.5", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz#7ba0697ca74c860ea13e3ba226b29617046982e2" + integrity sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew== + dependencies: + "@webassemblyjs/ast" "1.11.5" + "@webassemblyjs/helper-api-error" "1.11.5" + "@webassemblyjs/helper-wasm-bytecode" "1.11.5" + "@webassemblyjs/ieee754" "1.11.5" + "@webassemblyjs/leb128" "1.11.5" + "@webassemblyjs/utf8" "1.11.5" + +"@webassemblyjs/wast-printer@1.11.5": + version "1.11.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz#7a5e9689043f3eca82d544d7be7a8e6373a6fa98" + integrity sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA== + dependencies: + "@webassemblyjs/ast" "1.11.5" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -2367,7 +2383,7 @@ ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -2384,7 +2400,7 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: +ajv@^8.0.0, ajv@^8.9.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -2773,9 +2789,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: - version "1.0.30001478" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" - integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== + version "1.0.30001482" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz#8b3fad73dc35b2674a5c96df2d4f9f1c561435de" + integrity sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ== ccount@^1.0.0: version "1.1.0" @@ -2956,9 +2972,9 @@ colord@^2.9.1: integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== colorette@^2.0.10: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== combine-promises@^1.1.0: version "1.1.0" @@ -3113,11 +3129,6 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cosmiconfig-typescript-loader@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" - integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -3441,9 +3452,9 @@ dns-equal@^1.0.0: integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== dns-packet@^5.2.2: - version "5.5.0" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.5.0.tgz#f59cbf3396c130957c56a6ad5fd3959ccdc30065" - integrity sha512-USawdAUzRkV6xrqTjiAEp6M9YagZEzWcSUaZTcIFAiyQWW1SoI6KyId8y2+/71wbgHKQAKd+iupLv4YvEwYWvA== + version "5.6.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.0.tgz#2202c947845c7a63c23ece58f2f70ff6ab4c2f7d" + integrity sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ== dependencies: "@leichtgewicht/ip-codec" "^2.0.1" @@ -3484,7 +3495,7 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: +domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== @@ -3501,13 +3512,13 @@ domutils@^2.5.2, domutils@^2.8.0: domhandler "^4.2.0" domutils@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" - integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== dependencies: dom-serializer "^2.0.0" domelementtype "^2.3.0" - domhandler "^5.0.1" + domhandler "^5.0.3" dot-case@^3.0.4: version "3.0.4" @@ -3545,9 +3556,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.284: - version "1.4.363" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.363.tgz#a3d51d16169d8f212f00bafb78cf0667e24c3647" - integrity sha512-ReX5qgmSU7ybhzMuMdlJAdYnRhT90UB3k9M05O5nF5WH3wR5wgdJjXw0uDeFyKNhmglmQiOxkAbzrP0hMKM59g== + version "1.4.380" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.380.tgz#195dc59d930c6b74efbee6f0e6a267ce4af5ed91" + integrity sha512-XKGdI4pWM78eLH2cbXJHiBnWUwFSzZM7XujsB6stDiGu9AeSqziedP6amNLpJzE3i0rLTcfAwdCTs5ecP5yeSg== emoji-regex@^8.0.0: version "8.0.0" @@ -3581,10 +3592,10 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.10.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== +enhanced-resolve@^5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" + integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -4322,9 +4333,9 @@ html-void-elements@^1.0.0: integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== html-webpack-plugin@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" - integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + version "5.5.1" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz#826838e31b427f5f7f30971f8d8fa2422dfa6763" + integrity sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA== dependencies: "@types/html-minifier-terser" "^6.0.0" html-minifier-terser "^6.0.2" @@ -4754,10 +4765,15 @@ jest-worker@^29.1.2: merge-stream "^2.0.0" supports-color "^8.0.0" +jiti@^1.18.2: + version "1.18.2" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" + integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== + joi@^17.6.0: - version "17.9.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" - integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== + version "17.9.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" + integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -5051,9 +5067,9 @@ media-typer@0.3.0: integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== memfs@^3.1.2, memfs@^3.4.3: - version "3.5.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.0.tgz#9da86405fca0a539addafd37dbd452344fd1c0bd" - integrity sha512-yK6o8xVJlQerz57kvPROwTMgx5WtGwC2ZxDtOUsnGl49rHjYkfQoPNZPCKH73VdLE1BwBu/+Fx/NL8NYMUw2aA== + version "3.5.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.1.tgz#f0cd1e2bfaef58f6fe09bfb9c2288f07fea099ec" + integrity sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA== dependencies: fs-monkey "^1.0.3" @@ -5176,7 +5192,7 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@^3.3.4: +nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== @@ -5607,12 +5623,12 @@ postcss-discard-unused@^5.1.0: postcss-selector-parser "^6.0.5" postcss-loader@^7.0.0: - version "7.2.4" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.2.4.tgz#2884f4ca172de633b2cf1f93dc852968f0632ba9" - integrity sha512-F88rpxxNspo5hatIc+orYwZDtHFaVFOSIVAx+fBfJC1GmhWbVmPWtmg2gXKE1OxJbneOSGn8PWdIwsZFcruS+w== + version "7.3.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.0.tgz#05991c1e490d8ff86ef18358d87db3b5b2dcb5f5" + integrity sha512-qLAFjvR2BFNz1H930P7mj1iuWJFjGey/nVhimfOAAQ1ZyPpcClAxP8+A55Sl8mBvM+K2a9Pjgdj10KpANWrNfw== dependencies: cosmiconfig "^8.1.3" - cosmiconfig-typescript-loader "^4.3.0" + jiti "^1.18.2" klona "^2.0.6" semver "^7.3.8" @@ -5796,17 +5812,17 @@ postcss-reduce-transforms@^5.1.0: postcss-value-parser "^4.2.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" - integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + version "6.0.12" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz#2efae5ffab3c8bfb2b7fbf0c426e3bca616c4abb" + integrity sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" postcss-sort-media-queries@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz#f48a77d6ce379e86676fc3f140cf1b10a06f6051" - integrity sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg== + version "4.4.1" + resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz#04a5a78db3921eb78f28a1a781a2e68e65258128" + integrity sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw== dependencies: sort-css-media-queries "2.1.0" @@ -5836,11 +5852,11 @@ postcss-zindex@^5.1.0: integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.19: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== dependencies: - nanoid "^3.3.4" + nanoid "^3.3.6" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -6415,9 +6431,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^7.5.4: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -6467,24 +6483,24 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.2.tgz#36c10abca6f7577aeae136c804b0c741edeadc99" + integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.1.tgz#eb2d042df8b01f4b5c276a2dfd41ba0faab72e8d" + integrity sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + ajv-keywords "^5.1.0" section-matter@^1.0.0: version "1.0.0" @@ -6524,9 +6540,9 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: - version "7.4.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" - integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== dependencies: lru-cache "^6.0.0" @@ -6945,9 +6961,9 @@ terser-webpack-plugin@^5.3.3, terser-webpack-plugin@^5.3.7: terser "^5.16.5" terser@^5.10.0, terser@^5.16.5: - version "5.16.9" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.9.tgz#7a28cb178e330c484369886f2afd623d9847495f" - integrity sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg== + version "5.17.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" + integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -7192,9 +7208,9 @@ unpipe@1.0.0, unpipe@~1.0.0: integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -7386,9 +7402,9 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.9.3: - version "4.13.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz#d97445481d78691efe6d9a3b230833d802fc31f9" - integrity sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw== + version "4.13.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.3.tgz#9feb740b8b56b886260bae1360286818a221bae8" + integrity sha512-KqqzrzMRSRy5ePz10VhjyL27K2dxqwXQLP5rAKwRJBPUahe7Z2bBWzHw37jeb8GCPKxZRO79ZdQUAPesMh/Nug== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -7435,20 +7451,20 @@ webpack-sources@^3.2.2, webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.73.0: - version "5.79.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.79.0.tgz#8552b5da5a26e4e25842c08a883e08fc7740547a" - integrity sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg== + version "5.81.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.81.0.tgz#27a2e8466c8b4820d800a8d90f06ef98294f9956" + integrity sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" + enhanced-resolve "^5.13.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -7458,7 +7474,7 @@ webpack@^5.73.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.1.2" tapable "^2.1.1" terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" @@ -7525,9 +7541,9 @@ widest-line@^4.0.1: string-width "^5.0.1" wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== wrap-ansi@^7.0.0: version "7.0.0" From c615a618af0f1eaaa3bcf87eb0e6ad4db39d2400 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 09:05:24 +0200 Subject: [PATCH 163/209] change sh with bash --- etc/dbus-serialbattery/reinstall-local.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/reinstall-local.sh b/etc/dbus-serialbattery/reinstall-local.sh index f29fd4c3..aba7436a 100755 --- a/etc/dbus-serialbattery/reinstall-local.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -6,7 +6,7 @@ DRIVERNAME=dbus-serialbattery # handle read only mounts -sh /opt/victronenergy/swupdate-scripts/remount-rw.sh +bash /opt/victronenergy/swupdate-scripts/remount-rw.sh # install rm -rf /opt/victronenergy/service/$DRIVERNAME @@ -17,7 +17,7 @@ mkdir /opt/victronenergy/$DRIVERNAME/bms cp -f /data/etc/$DRIVERNAME/* /opt/victronenergy/$DRIVERNAME &>/dev/null cp -f /data/etc/$DRIVERNAME/bms/* /opt/victronenergy/$DRIVERNAME/bms &>/dev/null cp -rf /data/etc/$DRIVERNAME/service /opt/victronenergy/service-templates/$DRIVERNAME -sh /data/etc/$DRIVERNAME/install-qml.sh +bash /data/etc/$DRIVERNAME/install-qml.sh # check if serial-starter.d was deleted serialstarter_path="/data/conf/serial-starter.d" From 43368fe1a21bfc793424941e2492de3e98b14712 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 09:42:15 +0200 Subject: [PATCH 164/209] limitation reason cleanup --- etc/dbus-serialbattery/battery.py | 110 +++++++----------------------- 1 file changed, 25 insertions(+), 85 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 8fd98336..22414a9f 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -326,58 +326,35 @@ def manage_charge_voltage_step(self) -> None: def manage_charge_current(self) -> None: # Manage Charge Current Limitations - charge_limits = [ - self.max_battery_charge_current - ] # gets removed after finished testing - charge_limits_new = {utils.MAX_BATTERY_CHARGE_CURRENT: "Config Limit"} + charge_limits = {utils.MAX_BATTERY_CHARGE_CURRENT: "Config Limit"} # if values are not the same, then the limit was read also from the BMS if utils.MAX_BATTERY_CHARGE_CURRENT != self.max_battery_charge_current: - charge_limits_new.update({self.max_battery_charge_current: "BMS Limit"}) + charge_limits.update({self.max_battery_charge_current: "BMS Limit"}) if utils.CCCM_CV_ENABLE: tmp = self.calcMaxChargeCurrentReferringToCellVoltage() - charge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_charge_current: " - # + str(self.max_battery_charge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_charge_current != tmp: - if tmp in charge_limits_new: - charge_limits_new.update( - {tmp: charge_limits_new[tmp] + ", Cell Voltage"} - ) + if tmp in charge_limits: + charge_limits.update({tmp: charge_limits[tmp] + ", Cell Voltage"}) else: - charge_limits_new.update({tmp: "Cell Voltage"}) + charge_limits.update({tmp: "Cell Voltage"}) if utils.CCCM_T_ENABLE: tmp = self.calcMaxChargeCurrentReferringToTemperature() - charge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_charge_current: " - # + str(self.max_battery_charge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_charge_current != tmp: - if tmp in charge_limits_new: - charge_limits_new.update({tmp: charge_limits_new[tmp] + ", Temp"}) + if tmp in charge_limits: + charge_limits.update({tmp: charge_limits[tmp] + ", Temp"}) else: - charge_limits_new.update({tmp: "Temp"}) + charge_limits.update({tmp: "Temp"}) if utils.CCCM_SOC_ENABLE: tmp = self.calcMaxChargeCurrentReferringToSoc() - charge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_charge_current: " - # + str(self.max_battery_charge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_charge_current != tmp: - if tmp in charge_limits_new: - charge_limits_new.update({tmp: charge_limits_new[tmp] + ", SoC"}) + if tmp in charge_limits: + charge_limits.update({tmp: charge_limits[tmp] + ", SoC"}) else: - charge_limits_new.update({tmp: "SoC"}) + charge_limits.update({tmp: "SoC"}) # do not set CCL immediately, but only # - after LINEAR_RECALCULATION_EVERY passed @@ -403,12 +380,7 @@ def manage_charge_current(self) -> None: self.control_charge_current = ccl - self.charge_limitation = ( - charge_limits_new[min(charge_limits_new)] - # + " (" - # + str(round(min(charge_limits_new), 3)) - # + ")" - ) + self.charge_limitation = charge_limits[min(charge_limits)] if self.control_charge_current == 0: self.control_allow_charge = False @@ -418,64 +390,37 @@ def manage_charge_current(self) -> None: ##### # Manage Discharge Current Limitations - discharge_limits = [ - self.max_battery_discharge_current - ] # gets removed after finished testing - discharge_limits_new = {utils.MAX_BATTERY_DISCHARGE_CURRENT: "Config Limit"} + discharge_limits = {utils.MAX_BATTERY_DISCHARGE_CURRENT: "Config Limit"} # if values are not the same, then the limit was read also from the BMS if utils.MAX_BATTERY_DISCHARGE_CURRENT != self.max_battery_discharge_current: - discharge_limits_new.update( - {self.max_battery_discharge_current: "BMS Limit"} - ) + discharge_limits.update({self.max_battery_discharge_current: "BMS Limit"}) if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() - discharge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_discharge_current: " - # + str(self.max_battery_discharge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_discharge_current != tmp: - if tmp in discharge_limits_new: - discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", Cell Voltage"} + if tmp in discharge_limits: + discharge_limits.update( + {tmp: discharge_limits[tmp] + ", Cell Voltage"} ) else: - discharge_limits_new.update({tmp: "Cell Voltage"}) + discharge_limits.update({tmp: "Cell Voltage"}) if utils.DCCM_T_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToTemperature() - discharge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_discharge_current: " - # + str(self.max_battery_discharge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_discharge_current != tmp: - if tmp in discharge_limits_new: - discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", Temp"} - ) + if tmp in discharge_limits: + discharge_limits.update({tmp: discharge_limits[tmp] + ", Temp"}) else: - discharge_limits_new.update({tmp: "Temp"}) + discharge_limits.update({tmp: "Temp"}) if utils.DCCM_SOC_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToSoc() - discharge_limits.append(tmp) # gets removed after finished testing - - # logging.error("self.max_battery_discharge_current: " - # + str(self.max_battery_discharge_current) - # + " - tmp: " - # + str(tmp)) if self.max_battery_discharge_current != tmp: - if tmp in discharge_limits_new: - discharge_limits_new.update( - {tmp: discharge_limits_new[tmp] + ", SoC"} - ) + if tmp in discharge_limits: + discharge_limits.update({tmp: discharge_limits[tmp] + ", SoC"}) else: - discharge_limits_new.update({tmp: "SoC"}) + discharge_limits.update({tmp: "SoC"}) # do not set DCL immediately, but only # - after LINEAR_RECALCULATION_EVERY passed @@ -501,12 +446,7 @@ def manage_charge_current(self) -> None: self.control_discharge_current = dcl - self.discharge_limitation = ( - discharge_limits_new[min(discharge_limits_new)] - # + " (" - # + str(round(min(discharge_limits_new), 3)) - # + ")" - ) + self.discharge_limitation = discharge_limits[min(discharge_limits)] if self.control_discharge_current == 0: self.control_allow_discharge = False From ad2855d19f97d7ca0857b5c4ffd54cc026201185 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 09:55:00 +0200 Subject: [PATCH 165/209] changed default config settings FLOAT_CELL_VOLTAGE from 3.350V to 3.375V LINEAR_LIMITATION_ENABLE from False to True --- CHANGELOG.md | 2 ++ etc/dbus-serialbattery/config.default.ini | 23 ++++++++++++----------- etc/dbus-serialbattery/utils.py | 19 ++++++++++--------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5797935f..92ea31c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ * Changed TimeToSoc default value `TIME_TO_SOC_VALUE_TYPE` from `Both seconds and time string " [d h m s]"` to `1 Seconds` by @mr-manuel * Changed TimeToSoc description by @mr-manuel * Changed value positions, added groups and much clearer descriptions by @mr-manuel +* Changed: Default FLOAT_CELL_VOLTAGE from 3.350 V to 3.375 V by @mr-manuel +* Changed: Default LINEAR_LIMITATION_ENABLE from False to True by @mr-manuel * Changed: Disabled ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/239 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/311 by @mr-manuel diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 3d409093..edcdce3b 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -1,21 +1,30 @@ [DEFAULT] -; Battery Current limits +; --------- Battery Current limits --------- MAX_BATTERY_CHARGE_CURRENT = 50.0 MAX_BATTERY_DISCHARGE_CURRENT = 60.0 -; Bluetooth BMS +; --------- Cell Voltages --------- +; Description: Cell min/max voltages which are used to calculate the min/max battery voltage +; Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage +MIN_CELL_VOLTAGE = 2.900 +; Max voltage can seen as absorption voltage +MAX_CELL_VOLTAGE = 3.450 +FLOAT_CELL_VOLTAGE = 3.375 + +; --------- Bluetooth BMS --------- ; Description: List the Bluetooth BMS here that you want to install ; Example with 1 BMS: Jkbms_Ble C8:47:8C:00:00:00 ; Example with 3 BMS: Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:00:00:11, Jkbms_Ble C8:47:8C:00:00:22 #BLUETOOTH_BMS = Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:12:34:BB , Jkbms_Ble C8:47:8C:12:34:CC, Jkbms_Ble C8:47:8C:12:34:DD BLUETOOTH_BMS = +; --------- Charge mode --------- ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode. This is the default with limitations on hard boundary steps ; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) ; For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded -LINEAR_LIMITATION_ENABLE = False +LINEAR_LIMITATION_ENABLE = True ; Specify in seconds how often the linear values should be recalculated LINEAR_RECALCULATION_EVERY = 60 @@ -39,14 +48,6 @@ LINEAR_RECALCULATION_ON_PERC_CHANGE = 5 ; Charge voltage control management enable (True/False). CVCM_ENABLE = True -; -- Cell Voltages -; Description: Cell min/max voltages which are used to calculate the min/max battery voltage -; Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage -MIN_CELL_VOLTAGE = 2.90 -; Max voltage can seen as absorption voltage -MAX_CELL_VOLTAGE = 3.45 -FLOAT_CELL_VOLTAGE = 3.35 - ; -- CVL reset based on cell voltage diff (linear mode) ; Specify cell voltage diff where CVL limit is kept until diff is equal or lower CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = 0.010 diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 0d94e36b..74d6e58a 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -40,12 +40,21 @@ def _get_list_from_config( zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" -# Battery Current limits +# --------- Battery Current limits --------- MAX_BATTERY_CHARGE_CURRENT = float(config["DEFAULT"]["MAX_BATTERY_CHARGE_CURRENT"]) MAX_BATTERY_DISCHARGE_CURRENT = float( config["DEFAULT"]["MAX_BATTERY_DISCHARGE_CURRENT"] ) +# --------- Cell Voltages --------- +# Description: Cell min/max voltages which are used to calculate the min/max battery voltage +# Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage +MIN_CELL_VOLTAGE = float(config["DEFAULT"]["MIN_CELL_VOLTAGE"]) +MAX_CELL_VOLTAGE = float(config["DEFAULT"]["MAX_CELL_VOLTAGE"]) +# Max voltage can seen as absorption voltage +FLOAT_CELL_VOLTAGE = float(config["DEFAULT"]["FLOAT_CELL_VOLTAGE"]) + +# --------- Charge mode --------- # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps # True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) @@ -76,14 +85,6 @@ def _get_list_from_config( # Charge voltage control management enable (True/False). CVCM_ENABLE = "True" == config["DEFAULT"]["CVCM_ENABLE"] -# -- Cell Voltages -# Description: Cell min/max voltages which are used to calculate the min/max battery voltage -# Example: 16 cells * 3.45V/cell = 55.2V max charge voltage. 16 cells * 2.90V = 46.4V min discharge voltage -MIN_CELL_VOLTAGE = float(config["DEFAULT"]["MIN_CELL_VOLTAGE"]) -MAX_CELL_VOLTAGE = float(config["DEFAULT"]["MAX_CELL_VOLTAGE"]) -# Max voltage can seen as absorption voltage -FLOAT_CELL_VOLTAGE = float(config["DEFAULT"]["FLOAT_CELL_VOLTAGE"]) - # -- CVL reset based on cell voltage diff (linear mode) # Specify cell voltage diff where CVL limit is kept until diff is equal or lower CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL = float( From 66f982ae1b85cbac2090be0243957e252f5b8084 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 10:14:00 +0200 Subject: [PATCH 166/209] removed testing line --- etc/dbus-serialbattery/config.default.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index edcdce3b..f2ac2ab4 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -16,7 +16,6 @@ FLOAT_CELL_VOLTAGE = 3.375 ; Description: List the Bluetooth BMS here that you want to install ; Example with 1 BMS: Jkbms_Ble C8:47:8C:00:00:00 ; Example with 3 BMS: Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:00:00:11, Jkbms_Ble C8:47:8C:00:00:22 -#BLUETOOTH_BMS = Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:12:34:BB , Jkbms_Ble C8:47:8C:12:34:CC, Jkbms_Ble C8:47:8C:12:34:DD BLUETOOTH_BMS = ; --------- Charge mode --------- From 524321cd3c3c62080e0bf4cd4bbfcffaa9508a04 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 10:23:52 +0200 Subject: [PATCH 167/209] Cleanup duplicated files Files were moved and not deleted --- etc/dbus-serialbattery/hlpdatabms4s.py | 241 ---- .../hlpdatabms4s_miniterm.py | 1190 ----------------- etc/dbus-serialbattery/seplos.py | 298 ----- 3 files changed, 1729 deletions(-) delete mode 100644 etc/dbus-serialbattery/hlpdatabms4s.py delete mode 100644 etc/dbus-serialbattery/hlpdatabms4s_miniterm.py delete mode 100644 etc/dbus-serialbattery/seplos.py diff --git a/etc/dbus-serialbattery/hlpdatabms4s.py b/etc/dbus-serialbattery/hlpdatabms4s.py deleted file mode 100644 index 1bacf395..00000000 --- a/etc/dbus-serialbattery/hlpdatabms4s.py +++ /dev/null @@ -1,241 +0,0 @@ -# -*- coding: utf-8 -*- -from battery import Battery, Cell -from utils import logger -import utils -import serial -from time import sleep - - -class HLPdataBMS4S(Battery): - def __init__(self, port, baud, address): - super(HLPdataBMS4S, self).__init__(port, baud, address) - self.type = self.BATTERYTYPE - - BATTERYTYPE = "HLPdataBMS4S" - - def test_connection(self): - # call a function that will connect to the battery, send a command and retrieve the result. - # The result or call should be unique to this BMS. Battery name or version, etc. - # Return True if success, False for failure - result = False - try: - result = self.read_test_data() - except Exception as err: - logger.error(f"Unexpected {err=}, {type(err)=}") - result = False - - return result - - def get_settings(self): - # After successful connection get_settings will be call to set up the battery. - # Set the current limits, populate cell count, etc - # Return True if success, False for failure - result = False - try: - result = self.read_settings_data() - except Exception as e: - logger.error(e, exc_info=True) - pass - return result - - def refresh_data(self): - # call all functions that will refresh the battery data. - # This will be called for every iteration (1 second) - # Return True if success, False for failure - result = False - try: - result = self.read_status_data() - except Exception as e: - logger.error(e, exc_info=True) - pass - return result - - # def log_settings(self): - # logger.info(f'Battery {self.type} connected to dbus from {self.port}') - # logger.info(f'=== Settings ===') - # cell_counter = len(self.cells) - # logger.info(f'> Connection voltage {self.voltage}V | current {self.current}A | SOC {self.soc}%') - # logger.info(f'> Cell count {self.cell_count} | cells populated {cell_counter}') - # logger.info(f'> CCCM SOC {CCCM_SOC_ENABLE} | DCCM SOC {DCCM_SOC_ENABLE}') - # logger.info(f'> CCCM CV {CCCM_CV_ENABLE} | DCCM CV {DCCM_CV_ENABLE}') - # logger.info(f'> CCCM T {CCCM_T_ENABLE} | DCCM T {DCCM_T_ENABLE}') - # logger.info(f'> MIN_CELL_VOLTAGE {MIN_CELL_VOLTAGE}V | MAX_CELL_VOLTAGE {MAX_CELL_VOLTAGE}V') - - return - - def read_test_data(self): - test_data = self.read_serial_data_HLPdataBMS4S(b"pv\n", 1, 15) - if test_data is False: - return False - s1 = str(test_data) - ix = s1.find("BMS4S") - if ix > 0: - self.hardware_version = s1[ix : len(s1) - 1] - self.version = self.hardware_version - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT - self.poll_interval = 10000 - self.control_discharge_current = 1000 - self.control_charge_current = 1000 - self.soc = 50 - self.voltage = 13.2 - self.current = 0 - self.min_battery_voltage = 12.0 - self.max_battery_voltage = 14.4 - - if self.cell_count is None: - self.cell_count = 4 - for c in range(self.cell_count): - self.cells.append(Cell(False)) - return True - return False - - def read_settings_data(self): - test_data = self.read_serial_data_HLPdataBMS4S(b"ps\n", 3, 700) - if test_data is False: - return False - s = str(test_data) - s = s.replace(",", ".") - par = get_par("BatterySize= ", s) - if par is False: - return False - self.capacity = int(par) - v = get_par("VoltHigh= ", s) - if v is False: - return False - self.max_battery_voltage = float(v) * float(4) - v = get_par("VoltLow= ", s) - if v is False: - return False - self.min_battery_voltage = float(v) * float(4) - - return True - - def read_status_data(self): - status_data = self.read_serial_data_HLPdataBMS4S(b"m1\n", 0.2, 40) - if status_data is False: - return False - par1 = str(status_data) - par = par1.split(",") - if len(par) < 8: - return False - if len(par[0]) < 7: - return False - p0 = str(par[0]) - ix = p0.find(".") - par0 = p0[ix - 1 : len(p0)] - - # v1,v2,v3,v4,current,soc,chargeoff,loadoff,vbat2,socnow,adj,beep,led,temp1,temp2... - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14... - - self.voltage = float(par0) + float(par[1]) + float(par[2]) + float(par[3]) - self.cells[0].voltage = float(par0) - self.cells[1].voltage = float(par[1]) - self.cells[2].voltage = float(par[2]) - self.cells[3].voltage = float(par[3]) - self.current = float(par[4]) - self.soc = int(par[5]) - self.control_allow_charge = par[6] - self.charge_fet = par[6] - self.control_allow_discharge = par[7] - self.discharge_fet = par[7] - - beep = int(par[11]) - if beep == 2: - self.protection.temp_low_charge = 1 - else: - self.protection.temp_low_charge = 0 - if beep == 3: - self.protection.temp_high_charge = 1 - else: - self.protection.temp_high_charge = 0 - if beep == 4: - self.protection.voltage_low = 2 - else: - self.protection.voltage_low = 0 - if beep == 5: - self.protection.voltage_high = 2 - else: - self.protection.voltage_high = 0 - - if len(par) > 13: - nb = 0 - min = int(1000) - max = int(-1000) - ix = 13 - while ix < len(par): - tmp = par[ix].split(" ") - ix += 1 - if len(tmp) == 2: - name = tmp[0] - temp = int("".join(filter(str.isdigit, tmp[1]))) - if name[0] == "b": - nb += 1 - if temp > max: - max = temp - if temp < min: - min = temp - if nb == 1: - self.temp1 = max - if nb > 1: - self.temp1 = max - self.temp2 = min - - return True - - def manage_charge_voltage(self): - self.allow_max_voltage = True - self.control_voltage = self.max_battery_voltage - - def manage_charge_current(self): - self.control_charge_current = 1000 - self.control_discharge_current = 1000 - - def read_serial_data_HLPdataBMS4S(self, command, time, min_len): - data = read_serial_data2(command, self.port, self.baud_rate, time, min_len) - if data is False: - return False - return data - - -def read_serial_data2(command, port, baud, time, min_len): - try: - with serial.Serial(port, baudrate=baud, timeout=0.5) as ser: - ret = read_serialport_data2(ser, command, time, min_len) - if not ret is False: - return ret - return False - - except serial.SerialException as e: - logger.error(e) - return False - - -def read_serialport_data2(ser, command, time, min_len): - try: - cnt = 0 - while cnt < 3: - cnt += 1 - ser.flushOutput() - ser.flushInput() - ser.write(command) - sleep(time) - res = ser.read(1000) - if len(res) >= min_len: - return res - return False - - except serial.SerialException as e: - logger.error(e) - return False - - -def get_par(p, s): - ix = s.find(p) - if ix > 0: - ix += len(p) - for i in range(ix, len(s)): - if s[i] == " " or s[i] == 10 or s[i] == 13: - ret = s[ix:i] - return ret - return False diff --git a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py b/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py deleted file mode 100644 index 80cb5414..00000000 --- a/etc/dbus-serialbattery/hlpdatabms4s_miniterm.py +++ /dev/null @@ -1,1190 +0,0 @@ -#!/usr/bin/env python -# -# Very simple serial terminal -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C)2002-2020 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -# -# Modified to enable managing HLPdataBMS4S in Venus OS - -from __future__ import absolute_import - -import codecs -import os -import sys -import threading - -import serial -from serial.tools.list_ports import comports -from serial.tools import hexlify_codec - -# from time import sleep - -# pylint: disable=wrong-import-order,wrong-import-position - -codecs.register(lambda c: hexlify_codec.getregentry() if c == "hexlify" else None) - -try: - raw_input -except NameError: - # pylint: disable=redefined-builtin,invalid-name - raw_input = input # in python3 it's "raw" - unichr = chr - - -def key_description(character): - """generate a readable description for a key""" - ascii_code = ord(character) - if ascii_code < 32: - return "Ctrl+{:c}".format(ord("@") + ascii_code) - else: - return repr(character) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -class ConsoleBase(object): - """OS abstraction for console (input/output codec, no echo)""" - - def __init__(self, miniterm): - self.miniterm = miniterm - if sys.version_info >= (3, 0): - self.byte_output = sys.stdout.buffer - else: - self.byte_output = sys.stdout - self.output = sys.stdout - - def setup(self): - """Set console to read single characters, no echo""" - - def cleanup(self): - """Restore default console settings""" - - def getkey(self): - """Read a single key from the console""" - return None - - def write_bytes(self, byte_string): - """Write bytes (already encoded)""" - self.byte_output.write(byte_string) - self.byte_output.flush() - - def write(self, text): - """Write string""" - self.output.write(text) - self.output.flush() - - def cancel(self): - """Cancel getkey operation""" - - # - - - - - - - - - - - - - - - - - - - - - - - - - # context manager: - # switch terminal temporary to normal mode (e.g. to get user input) - - def __enter__(self): - self.cleanup() - return self - - def __exit__(self, *args, **kwargs): - self.setup() - - -if os.name == "nt": # noqa - import msvcrt - import ctypes - import platform - - class Out(object): - """file-like wrapper that uses os.write""" - - def __init__(self, fd): - self.fd = fd - - def flush(self): - pass - - def write(self, s): - os.write(self.fd, s) - - class Console(ConsoleBase): - fncodes = { - ";": "\x1bOP", # F1 - "<": "\x1bOQ", # F2 - "=": "\x1bOR", # F3 - ">": "\x1bOS", # F4 - "?": "\x1b[15~", # F5 - "@": "\x1b[17~", # F6 - "A": "\x1b[18~", # F7 - "B": "\x1b[19~", # F8 - "C": "\x1b[20~", # F9 - "D": "\x1b[21~", # F10 - } - navcodes = { - "H": "\x1b[A", # UP - "P": "\x1b[B", # DOWN - "K": "\x1b[D", # LEFT - "M": "\x1b[C", # RIGHT - "G": "\x1b[H", # HOME - "O": "\x1b[F", # END - "R": "\x1b[2~", # INSERT - "S": "\x1b[3~", # DELETE - "I": "\x1b[5~", # PAGE UP - "Q": "\x1b[6~", # PAGE DOWN - } - - def __init__(self, miniterm): - super(Console, self).__init__(miniterm) - self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() - self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() - ctypes.windll.kernel32.SetConsoleOutputCP(65001) - ctypes.windll.kernel32.SetConsoleCP(65001) - # ANSI handling available through SetConsoleMode since Windows 10 v1511 - # https://en.wikipedia.org/wiki/ANSI_escape_code#cite_note-win10th2-1 - if ( - platform.release() == "10" - and int(platform.version().split(".")[2]) > 10586 - ): - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 - import ctypes.wintypes as wintypes - - if not hasattr(wintypes, "LPDWORD"): # PY2 - wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD) - SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode - GetConsoleMode = ctypes.windll.kernel32.GetConsoleMode - GetStdHandle = ctypes.windll.kernel32.GetStdHandle - mode = wintypes.DWORD() - GetConsoleMode(GetStdHandle(-11), ctypes.byref(mode)) - if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: - SetConsoleMode( - GetStdHandle(-11), - mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING, - ) - self._saved_cm = mode - self.output = codecs.getwriter("UTF-8")(Out(sys.stdout.fileno()), "replace") - # the change of the code page is not propagated to Python, manually fix it - sys.stderr = codecs.getwriter("UTF-8")(Out(sys.stderr.fileno()), "replace") - sys.stdout = self.output - self.output.encoding = "UTF-8" # needed for input - - def __del__(self): - ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) - ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) - try: - ctypes.windll.kernel32.SetConsoleMode( - ctypes.windll.kernel32.GetStdHandle(-11), self._saved_cm - ) - except AttributeError: # in case no _saved_cm - pass - - def getkey(self): - while True: - z = msvcrt.getwch() - if z == unichr(13): - return unichr(10) - elif z is unichr(0) or z is unichr(0xE0): - try: - code = msvcrt.getwch() - if z is unichr(0): - return self.fncodes[code] - else: - return self.navcodes[code] - except KeyError: - pass - else: - return z - - def cancel(self): - # CancelIo, CancelSynchronousIo do not seem to work when using - # getwch, so instead, send a key to the window with the console - hwnd = ctypes.windll.kernel32.GetConsoleWindow() - ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0D, 0) - -elif os.name == "posix": - import atexit - import termios - import fcntl - import signal - - class Console(ConsoleBase): - def __init__(self, miniterm): - super(Console, self).__init__(miniterm) - self.fd = sys.stdin.fileno() - self.old = termios.tcgetattr(self.fd) - atexit.register(self.cleanup) - signal.signal(signal.SIGINT, self.sigint) - if sys.version_info < (3, 0): - self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) - else: - self.enc_stdin = sys.stdin - - def setup(self): - new = termios.tcgetattr(self.fd) - new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG - new[6][termios.VMIN] = 1 - new[6][termios.VTIME] = 0 - termios.tcsetattr(self.fd, termios.TCSANOW, new) - - def getkey(self): - c = self.enc_stdin.read(1) - if c == unichr(0x7F): - c = unichr(8) # map the BS key (which yields DEL) to backspace - return c - - def cancel(self): - fcntl.ioctl(self.fd, termios.TIOCSTI, b"\0") - - def cleanup(self): - termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) - - def sigint(self, sig, frame): - """signal handler for a clean exit on SIGINT""" - self.miniterm.stop() - self.cancel() - -else: - raise NotImplementedError( - "Sorry no implementation for your platform ({}) available.".format(sys.platform) - ) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -class Transform(object): - """do-nothing: forward all data unchanged""" - - def rx(self, text): - """text received from serial port""" - return text - - def tx(self, text): - """text to be sent to serial port""" - return text - - def echo(self, text): - """text to be sent but displayed on console""" - return text - - -class CRLF(Transform): - """ENTER sends CR+LF""" - - def tx(self, text): - return text.replace("\n", "\r\n") - - -class CR(Transform): - """ENTER sends CR""" - - def rx(self, text): - return text.replace("\r", "\n") - - def tx(self, text): - return text.replace("\n", "\r") - - -class LF(Transform): - """ENTER sends LF""" - - -class NoTerminal(Transform): - """remove typical terminal control codes from input""" - - REPLACEMENT_MAP = dict( - (x, 0x2400 + x) for x in range(32) if unichr(x) not in "\r\n\b\t" - ) - REPLACEMENT_MAP.update( - { - 0x7F: 0x2421, # DEL - 0x9B: 0x2425, # CSI - } - ) - - def rx(self, text): - return text.translate(self.REPLACEMENT_MAP) - - echo = rx - - -class NoControls(NoTerminal): - """Remove all control codes, incl. CR+LF""" - - REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) - REPLACEMENT_MAP.update( - { - 0x20: 0x2423, # visual space - 0x7F: 0x2421, # DEL - 0x9B: 0x2425, # CSI - } - ) - - -class Printable(Transform): - """Show decimal code for all non-ASCII characters and replace most control codes""" - - def rx(self, text): - r = [] - for c in text: - if " " <= c < "\x7f" or c in "\r\n\b\t": - r.append(c) - elif c < " ": - r.append(unichr(0x2400 + ord(c))) - else: - r.extend(unichr(0x2080 + ord(d) - 48) for d in "{:d}".format(ord(c))) - r.append(" ") - return "".join(r) - - echo = rx - - -class Colorize(Transform): - """Apply different colors for received and echo""" - - def __init__(self): - # XXX make it configurable, use colorama? - self.input_color = "\x1b[37m" - self.echo_color = "\x1b[31m" - - def rx(self, text): - return self.input_color + text - - def echo(self, text): - return self.echo_color + text - - -class DebugIO(Transform): - """Print what is sent and received""" - - def rx(self, text): - sys.stderr.write(" [RX:{!r}] ".format(text)) - sys.stderr.flush() - return text - - def tx(self, text): - sys.stderr.write(" [TX:{!r}] ".format(text)) - sys.stderr.flush() - return text - - -# other ideas: -# - add date/time for each newline -# - insert newline after: a) timeout b) packet end character - -EOL_TRANSFORMATIONS = { - "crlf": CRLF, - "cr": CR, - "lf": LF, -} - -TRANSFORMATIONS = { - "direct": Transform, # no transformation - "default": NoTerminal, - "nocontrol": NoControls, - "printable": Printable, - "colorize": Colorize, - "debug": DebugIO, -} - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def ask_for_port(): - """\ - Show a list of ports and ask the user for a choice. To make selection - easier on systems with long device names, also allow the input of an - index. - """ - sys.stderr.write("\n--- Available ports:\n") - ports = [] - for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): - sys.stderr.write("--- {:2}: {:20} {!r}\n".format(n, port, desc)) - ports.append(port) - while True: - sys.stderr.write("--- Enter port index or full name: ") - port = raw_input("") - try: - index = int(port) - 1 - if not 0 <= index < len(ports): - sys.stderr.write("--- Invalid index!\n") - continue - except ValueError: - pass - else: - port = ports[index] - return port - - -class Miniterm(object): - """\ - Terminal application. Copy data from serial port to console and vice versa. - Handle special keys from the console to show menu etc. - """ - - def __init__(self, serial_instance, echo=False, eol="crlf", filters=()): - self.console = Console(self) - self.serial = serial_instance - self.echo = True - self.raw = False - self.input_encoding = "UTF-8" - self.output_encoding = "UTF-8" - self.eol = eol - self.filters = filters - self.update_transformations() - self.exit_character = unichr(0x1D) # GS/CTRL+] - self.menu_character = unichr(0x14) # Menu: CTRL+T - self.alive = None - self._reader_alive = None - self.receiver_thread = None - self.rx_decoder = None - self.tx_decoder = None - self.tx_encoder = None - self.no_write = False - self.remove_no_write = False - - def _start_reader(self): - """Start reader thread""" - self._reader_alive = True - # start serial->console thread - self.receiver_thread = threading.Thread(target=self.reader, name="rx") - self.receiver_thread.daemon = True - self.receiver_thread.start() - - def _stop_reader(self): - """Stop reader thread only, wait for clean exit of thread""" - self._reader_alive = False - if hasattr(self.serial, "cancel_read"): - self.serial.cancel_read() - self.receiver_thread.join() - - def start(self): - """start worker threads""" - self.alive = True - self._start_reader() - # enter console->serial loop - self.transmitter_thread = threading.Thread(target=self.writer, name="tx") - self.transmitter_thread.daemon = True - self.transmitter_thread.start() - self.console.setup() - - def stop(self): - """set flag to stop worker threads""" - self.alive = False - - def join(self, transmit_only=False): - """wait for worker threads to terminate""" - self.transmitter_thread.join() - if not transmit_only: - if hasattr(self.serial, "cancel_read"): - self.serial.cancel_read() - self.receiver_thread.join() - - def close(self): - self.serial.close() - - def update_transformations(self): - """take list of transformation classes and instantiate them for rx and tx""" - transformations = [EOL_TRANSFORMATIONS[self.eol]] + [ - TRANSFORMATIONS[f] for f in self.filters - ] - self.tx_transformations = [t() for t in transformations] - self.rx_transformations = list(reversed(self.tx_transformations)) - - def set_rx_encoding(self, encoding, errors="replace"): - """set encoding for received data""" - self.input_encoding = encoding - self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) - - def set_tx_encoding(self, encoding, errors="replace"): - """set encoding for transmitted data""" - self.output_encoding = encoding - self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) - - def dump_port_settings(self): - """Write current settings to sys.stderr""" - sys.stderr.write( - "\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( - p=self.serial - ) - ) - sys.stderr.write( - "--- RTS: {:8} DTR: {:8} BREAK: {:8}\n".format( - ("active" if self.serial.rts else "inactive"), - ("active" if self.serial.dtr else "inactive"), - ("active" if self.serial.break_condition else "inactive"), - ) - ) - try: - sys.stderr.write( - "--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n".format( - ("active" if self.serial.cts else "inactive"), - ("active" if self.serial.dsr else "inactive"), - ("active" if self.serial.ri else "inactive"), - ("active" if self.serial.cd else "inactive"), - ) - ) - except serial.SerialException: - # on RFC 2217 ports, it can happen if no modem state notification was - # yet received. ignore this error. - pass - sys.stderr.write( - "--- software flow control: {}\n".format( - "active" if self.serial.xonxoff else "inactive" - ) - ) - sys.stderr.write( - "--- hardware flow control: {}\n".format( - "active" if self.serial.rtscts else "inactive" - ) - ) - sys.stderr.write("--- serial input encoding: {}\n".format(self.input_encoding)) - sys.stderr.write( - "--- serial output encoding: {}\n".format(self.output_encoding) - ) - sys.stderr.write("--- EOL: {}\n".format(self.eol.upper())) - sys.stderr.write("--- filters: {}\n".format(" ".join(self.filters))) - - def reader(self): - """loop and copy serial->console""" - data = b"" - while self.alive and self._reader_alive: - # read all that is there or wait for one byte - try: - data2 = self.serial.read(self.serial.in_waiting or 1) - except Exception: - data2 = b"" - if data2: - if self.remove_no_write is True: - self.remove_no_write = False - self.no_write = False - data = data2 - else: - data += data2 - if b"\n" in data: - if self.raw: - self.console.write_bytes(data) - else: - text = self.rx_decoder.decode(data) - for transformation in self.rx_transformations: - text = transformation.rx(text) - if b"m1\n" in data: - self.no_write = True - if self.no_write is False: - self.console.write(text) - data = b"" - - def writer(self): - """\ - Loop and copy console->serial until self.exit_character character is - found. When self.menu_character is found, interpret the next key - locally. - """ - menu_active = False - try: - while self.alive: - try: - c = self.console.getkey() - self.remove_no_write = True - except KeyboardInterrupt: - c = "\x03" - if not self.alive: - break - if menu_active: - self.handle_menu_key(c) - menu_active = False - elif c == self.menu_character: - menu_active = True # next char will be for menu - elif c == self.exit_character: - self.stop() # exit app - break - else: - # ~ if self.raw: - text = c - for transformation in self.tx_transformations: - text = transformation.tx(text) - self.serial.write(self.tx_encoder.encode(text)) - if self.echo: - echo_text = c - for transformation in self.tx_transformations: - echo_text = transformation.echo(echo_text) - self.console.write(echo_text) - except Exception: - self.alive = False - raise - - def handle_menu_key(self, c): - """Implement a simple menu / settings""" - if c == self.menu_character or c == self.exit_character: - # Menu/exit character again -> send itself - self.serial.write(self.tx_encoder.encode(c)) - if self.echo: - self.console.write(c) - elif c == "\x15": # CTRL+U -> upload file - self.upload_file() - elif c in "\x08hH?": # CTRL+H, h, H, ? -> Show help - sys.stderr.write(self.get_help_text()) - elif c == "\x12": # CTRL+R -> Toggle RTS - self.serial.rts = not self.serial.rts - sys.stderr.write( - "--- RTS {} ---\n".format("active" if self.serial.rts else "inactive") - ) - elif c == "\x04": # CTRL+D -> Toggle DTR - self.serial.dtr = not self.serial.dtr - sys.stderr.write( - "--- DTR {} ---\n".format("active" if self.serial.dtr else "inactive") - ) - elif c == "\x02": # CTRL+B -> toggle BREAK condition - self.serial.break_condition = not self.serial.break_condition - sys.stderr.write( - "--- BREAK {} ---\n".format( - "active" if self.serial.break_condition else "inactive" - ) - ) - elif c == "\x05": # CTRL+E -> toggle local echo - self.echo = not self.echo - sys.stderr.write( - "--- local echo {} ---\n".format("active" if self.echo else "inactive") - ) - elif c == "\x06": # CTRL+F -> edit filters - self.change_filter() - elif c == "\x0c": # CTRL+L -> EOL mode - modes = list(EOL_TRANSFORMATIONS) # keys - eol = modes.index(self.eol) + 1 - if eol >= len(modes): - eol = 0 - self.eol = modes[eol] - sys.stderr.write("--- EOL: {} ---\n".format(self.eol.upper())) - self.update_transformations() - elif c == "\x01": # CTRL+A -> set encoding - self.change_encoding() - elif c == "\x09": # CTRL+I -> info - self.dump_port_settings() - # ~ elif c == '\x01': # CTRL+A -> cycle escape mode - # ~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode - elif c in "pP": # P -> change port - self.change_port() - elif c in "zZ": # S -> suspend / open port temporarily - self.suspend_port() - elif c in "bB": # B -> change baudrate - self.change_baudrate() - elif c == "8": # 8 -> change to 8 bits - self.serial.bytesize = serial.EIGHTBITS - self.dump_port_settings() - elif c == "7": # 7 -> change to 8 bits - self.serial.bytesize = serial.SEVENBITS - self.dump_port_settings() - elif c in "eE": # E -> change to even parity - self.serial.parity = serial.PARITY_EVEN - self.dump_port_settings() - elif c in "oO": # O -> change to odd parity - self.serial.parity = serial.PARITY_ODD - self.dump_port_settings() - elif c in "mM": # M -> change to mark parity - self.serial.parity = serial.PARITY_MARK - self.dump_port_settings() - elif c in "sS": # S -> change to space parity - self.serial.parity = serial.PARITY_SPACE - self.dump_port_settings() - elif c in "nN": # N -> change to no parity - self.serial.parity = serial.PARITY_NONE - self.dump_port_settings() - elif c == "1": # 1 -> change to 1 stop bits - self.serial.stopbits = serial.STOPBITS_ONE - self.dump_port_settings() - elif c == "2": # 2 -> change to 2 stop bits - self.serial.stopbits = serial.STOPBITS_TWO - self.dump_port_settings() - elif c == "3": # 3 -> change to 1.5 stop bits - self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE - self.dump_port_settings() - elif c in "xX": # X -> change software flow control - self.serial.xonxoff = c == "X" - self.dump_port_settings() - elif c in "rR": # R -> change hardware flow control - self.serial.rtscts = c == "R" - self.dump_port_settings() - elif c in "qQ": - self.stop() # Q -> exit app - else: - sys.stderr.write( - "--- unknown menu character {} --\n".format(key_description(c)) - ) - - def upload_file(self): - """Ask user for filename and send its contents""" - sys.stderr.write("\n--- File to upload: ") - sys.stderr.flush() - with self.console: - filename = sys.stdin.readline().rstrip("\r\n") - if filename: - try: - with open(filename, "rb") as f: - sys.stderr.write("--- Sending file {} ---\n".format(filename)) - while True: - block = f.read(1024) - if not block: - break - self.serial.write(block) - # Wait for output buffer to drain. - self.serial.flush() - sys.stderr.write(".") # Progress indicator. - sys.stderr.write("\n--- File {} sent ---\n".format(filename)) - except IOError as e: - sys.stderr.write( - "--- ERROR opening file {}: {} ---\n".format(filename, e) - ) - - def change_filter(self): - """change the i/o transformations""" - sys.stderr.write("\n--- Available Filters:\n") - sys.stderr.write( - "\n".join( - "--- {:<10} = {.__doc__}".format(k, v) - for k, v in sorted(TRANSFORMATIONS.items()) - ) - ) - sys.stderr.write( - "\n--- Enter new filter name(s) [{}]: ".format(" ".join(self.filters)) - ) - with self.console: - new_filters = sys.stdin.readline().lower().split() - if new_filters: - for f in new_filters: - if f not in TRANSFORMATIONS: - sys.stderr.write("--- unknown filter: {!r}\n".format(f)) - break - else: - self.filters = new_filters - self.update_transformations() - sys.stderr.write("--- filters: {}\n".format(" ".join(self.filters))) - - def change_encoding(self): - """change encoding on the serial port""" - sys.stderr.write( - "\n--- Enter new encoding name [{}]: ".format(self.input_encoding) - ) - with self.console: - new_encoding = sys.stdin.readline().strip() - if new_encoding: - try: - codecs.lookup(new_encoding) - except LookupError: - sys.stderr.write("--- invalid encoding name: {}\n".format(new_encoding)) - else: - self.set_rx_encoding(new_encoding) - self.set_tx_encoding(new_encoding) - sys.stderr.write("--- serial input encoding: {}\n".format(self.input_encoding)) - sys.stderr.write( - "--- serial output encoding: {}\n".format(self.output_encoding) - ) - - def change_baudrate(self): - """change the baudrate""" - sys.stderr.write("\n--- Baudrate: ") - sys.stderr.flush() - with self.console: - backup = self.serial.baudrate - try: - self.serial.baudrate = int(sys.stdin.readline().strip()) - except ValueError as e: - sys.stderr.write("--- ERROR setting baudrate: {} ---\n".format(e)) - self.serial.baudrate = backup - else: - self.dump_port_settings() - - def change_port(self): - """Have a conversation with the user to change the serial port""" - with self.console: - try: - port = ask_for_port() - except KeyboardInterrupt: - port = None - if port and port != self.serial.port: - # reader thread needs to be shut down - self._stop_reader() - # save settings - settings = self.serial.getSettingsDict() - try: - new_serial = serial.serial_for_url(port, do_not_open=True) - # restore settings and open - new_serial.applySettingsDict(settings) - new_serial.rts = self.serial.rts - new_serial.dtr = self.serial.dtr - new_serial.open() - new_serial.break_condition = self.serial.break_condition - except Exception as e: - sys.stderr.write("--- ERROR opening new port: {} ---\n".format(e)) - new_serial.close() - else: - self.serial.close() - self.serial = new_serial - sys.stderr.write( - "--- Port changed to: {} ---\n".format(self.serial.port) - ) - # and restart the reader thread - self._start_reader() - - def suspend_port(self): - """\ - open port temporarily, allow reconnect, exit and port change to get - out of the loop - """ - # reader thread needs to be shut down - self._stop_reader() - self.serial.close() - sys.stderr.write("\n--- Port closed: {} ---\n".format(self.serial.port)) - do_change_port = False - while not self.serial.is_open: - sys.stderr.write( - "--- Quit: {exit} | p: port change | any other key to reconnect ---\n".format( - exit=key_description(self.exit_character) - ) - ) - k = self.console.getkey() - if k == self.exit_character: - self.stop() # exit app - break - elif k in "pP": - do_change_port = True - break - try: - self.serial.open() - except Exception as e: - sys.stderr.write("--- ERROR opening port: {} ---\n".format(e)) - if do_change_port: - self.change_port() - else: - # and restart the reader thread - self._start_reader() - sys.stderr.write("--- Port opened: {} ---\n".format(self.serial.port)) - - def get_help_text(self): - """return the help text""" - # help text, starts with blank line! - return """ ---- pySerial ({version}) - miniterm - help ---- ---- {exit:8} Exit program (alias {menu} Q) ---- {menu:8} Menu escape key, followed by: ---- Menu keys: ---- {menu:7} Send the menu character itself to remote ---- {exit:7} Send the exit character itself to remote ---- {info:7} Show info ---- {upload:7} Upload file (prompt will be shown) ---- {repr:7} encoding ---- {filter:7} edit filters ---- Toggles: ---- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK ---- {echo:7} echo {eol:7} EOL ---- ---- Port settings ({menu} followed by the following): ---- p change port ---- 7 8 set data bits ---- N E O S M change parity (None, Even, Odd, Space, Mark) ---- 1 2 3 set stop bits (1, 2, 1.5) ---- b change baud rate ---- x X disable/enable software flow control ---- r R disable/enable hardware flow control -""".format( - version=getattr(serial, "VERSION", "unknown version"), - exit=key_description(self.exit_character), - menu=key_description(self.menu_character), - rts=key_description("\x12"), - dtr=key_description("\x04"), - brk=key_description("\x02"), - echo=key_description("\x05"), - info=key_description("\x09"), - upload=key_description("\x15"), - repr=key_description("\x01"), - filter=key_description("\x06"), - eol=key_description("\x0c"), - ) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# default args can be used to override when calling main() from an other script -# e.g to create a miniterm-my-device.py -def main( - default_port=None, - default_baudrate=9600, - default_rts=None, - default_dtr=None, - serial_instance=None, -): - """Command line tool, entry point""" - - import argparse - - parser = argparse.ArgumentParser( - description="Miniterm - A simple terminal program for the serial port." - ) - - parser.add_argument( - "port", - nargs="?", - help='serial port name ("-" to show port list)', - default=default_port, - ) - - parser.add_argument( - "baudrate", - nargs="?", - type=int, - help="set baud rate, default: %(default)s", - default=default_baudrate, - ) - - group = parser.add_argument_group("port settings") - - group.add_argument( - "--parity", - choices=["N", "E", "O", "S", "M"], - type=lambda c: c.upper(), - help="set parity, one of {N E O S M}, default: N", - default="N", - ) - - group.add_argument( - "--rtscts", - action="store_true", - help="enable RTS/CTS flow control (default off)", - default=False, - ) - - group.add_argument( - "--xonxoff", - action="store_true", - help="enable software flow control (default off)", - default=False, - ) - - group.add_argument( - "--rts", - type=int, - help="set initial RTS line state (possible values: 0, 1)", - default=default_rts, - ) - - group.add_argument( - "--dtr", - type=int, - help="set initial DTR line state (possible values: 0, 1)", - default=default_dtr, - ) - - group.add_argument( - "--non-exclusive", - dest="exclusive", - action="store_false", - help="disable locking for native ports", - default=True, - ) - - group.add_argument( - "--ask", - action="store_true", - help="ask again for port when open fails", - default=False, - ) - - group = parser.add_argument_group("data handling") - - group.add_argument( - "-e", - "--echo", - action="store_true", - help="enable local echo (default off)", - default=False, - ) - - group.add_argument( - "--encoding", - dest="serial_port_encoding", - metavar="CODEC", - help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", - default="UTF-8", - ) - - group.add_argument( - "-f", - "--filter", - action="append", - metavar="NAME", - help="add text transformation", - default=[], - ) - - group.add_argument( - "--eol", - choices=["CR", "LF", "CRLF"], - type=lambda c: c.upper(), - help="end of line mode", - default="CRLF", - ) - - group.add_argument( - "--raw", - action="store_true", - help="Do no apply any encodings/transformations", - default=False, - ) - - group = parser.add_argument_group("hotkeys") - - group.add_argument( - "--exit-char", - type=int, - metavar="NUM", - help="Unicode of special character that is used to exit the application, default: %(default)s", - default=0x1D, - ) # GS/CTRL+] - - group.add_argument( - "--menu-char", - type=int, - metavar="NUM", - help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", - default=0x14, - ) # Menu: CTRL+T - - group = parser.add_argument_group("diagnostics") - - group.add_argument( - "-q", - "--quiet", - action="store_true", - help="suppress non-error messages", - default=False, - ) - - group.add_argument( - "--develop", - action="store_true", - help="show Python traceback on error", - default=False, - ) - - args = parser.parse_args() - - if args.menu_char == args.exit_char: - parser.error("--exit-char can not be the same as --menu-char") - - if args.filter: - if "help" in args.filter: - sys.stderr.write("Available filters:\n") - sys.stderr.write( - "\n".join( - "{:<10} = {.__doc__}".format(k, v) - for k, v in sorted(TRANSFORMATIONS.items()) - ) - ) - sys.stderr.write("\n") - sys.exit(1) - filters = args.filter - else: - filters = ["default"] - - while serial_instance is None: - # no port given on command line -> ask user now - if args.port is None or args.port == "-": - try: - args.port = ask_for_port() - except KeyboardInterrupt: - sys.stderr.write("\n") - parser.error("user aborted and port is not given") - else: - if not args.port: - parser.error("port is not given") - try: - serial_instance = serial.serial_for_url( - args.port, - args.baudrate, - parity=args.parity, - rtscts=args.rtscts, - xonxoff=args.xonxoff, - do_not_open=True, - ) - - if not hasattr(serial_instance, "cancel_read"): - # enable timeout for alive flag polling if cancel_read is not available - serial_instance.timeout = 1 - - if args.dtr is not None: - if not args.quiet: - sys.stderr.write( - "--- forcing DTR {}\n".format( - "active" if args.dtr else "inactive" - ) - ) - serial_instance.dtr = args.dtr - if args.rts is not None: - if not args.quiet: - sys.stderr.write( - "--- forcing RTS {}\n".format( - "active" if args.rts else "inactive" - ) - ) - serial_instance.rts = args.rts - - if isinstance(serial_instance, serial.Serial): - serial_instance.exclusive = args.exclusive - - serial_instance.open() - except serial.SerialException as e: - sys.stderr.write("could not open port {!r}: {}\n".format(args.port, e)) - if args.develop: - raise - if not args.ask: - sys.exit(1) - else: - args.port = "-" - else: - break - - miniterm = Miniterm( - serial_instance, echo=args.echo, eol=args.eol.lower(), filters=filters - ) - miniterm.exit_character = unichr(args.exit_char) - miniterm.menu_character = unichr(args.menu_char) - miniterm.raw = args.raw - miniterm.set_rx_encoding(args.serial_port_encoding) - miniterm.set_tx_encoding(args.serial_port_encoding) - - if not args.quiet: - sys.stderr.write( - "--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n".format( - p=miniterm.serial - ) - ) - sys.stderr.write( - "--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n".format( - key_description(miniterm.exit_character), - key_description(miniterm.menu_character), - key_description(miniterm.menu_character), - key_description("\x08"), - ) - ) - sys.stderr.write( - "--- Specifically modified for managing HLPdataBMS4S in Venus OS ---\n".format( # noqa: F522 - p=miniterm.serial - ) - ) - sys.stderr.write( - "--- Quit: Ctrl-t q | Local echo: Ctrl-t Ctrl-e ---\n".format( # noqa: F522 - p=miniterm.serial - ) - ) - - miniterm.start() - try: - miniterm.join(True) - except KeyboardInterrupt: - pass - if not args.quiet: - sys.stderr.write("\n--- exit ---\n") - miniterm.join() - miniterm.close() - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -if __name__ == "__main__": - main() diff --git a/etc/dbus-serialbattery/seplos.py b/etc/dbus-serialbattery/seplos.py deleted file mode 100644 index c9519fd0..00000000 --- a/etc/dbus-serialbattery/seplos.py +++ /dev/null @@ -1,298 +0,0 @@ -# -*- coding: utf-8 -*- -from battery import Protection, Battery, Cell -from utils import logger -import utils -import serial - - - -class Seplos(Battery): - def __init__(self, port, baud, address=0x00): - super(Seplos, self).__init__(port, baud, address) - self.type = self.BATTERYTYPE - self.poll_interval = 5000 - - BATTERYTYPE = "Seplos" - - COMMAND_STATUS = 0x42 - COMMAND_ALARM = 0x44 - COMMAND_PROTOCOL_VERSION = 0x4F - COMMAND_VENDOR_INFO = 0x51 - - @staticmethod - def int_from_1byte_hex_ascii(data: bytes, offset: int, signed=False): - return int.from_bytes( - bytes.fromhex(data[offset : offset + 2].decode("ascii")), - byteorder="big", - signed=signed, - ) - - @staticmethod - def int_from_2byte_hex_ascii(data: bytes, offset: int, signed=False): - return int.from_bytes( - bytes.fromhex(data[offset : offset + 4].decode("ascii")), - byteorder="big", - signed=signed, - ) - - @staticmethod - def get_checksum(frame: bytes) -> int: - """implements the Seplos checksum algorithm, returns 4 bytes""" - checksum = 0 - for b in frame: - checksum += b - checksum %= 0xFFFF - checksum ^= 0xFFFF - checksum += 1 - return checksum - - @staticmethod - def get_info_length(info: bytes) -> int: - """implements the Seplos checksum for the info length""" - lenid = len(info) - if lenid == 0: - return 0 - - lchksum = (lenid & 0xF) + ((lenid >> 4) & 0xF) + ((lenid >> 8) & 0xF) - lchksum %= 16 - lchksum ^= 0xF - lchksum += 1 - - return (lchksum << 12) + lenid - - @staticmethod - def encode_cmd(address: int, cid2: int, info: bytes = b"") -> bytes: - """encodes a command sent to a battery (cid1=0x46)""" - cid1 = 0x46 - - info_length = Seplos.get_info_length(info) - - frame = "{:02X}{:02X}{:02X}{:02X}{:04X}".format( - 0x20, address, cid1, cid2, info_length - ).encode() - frame += info - - checksum = Seplos.get_checksum(frame) - encoded = b"~" + frame + "{:04X}".format(checksum).encode() + b"\r" - return encoded - - def test_connection(self): - # call a function that will connect to the battery, send a command and retrieve the result. - # The result or call should be unique to this BMS. Battery name or version, etc. - # Return True if success, False for failure - - try: - return self.read_status_data() - except Exception as err: - logger.error(f"Unexpected {err=}, {type(err)=}") - return False - - def get_settings(self): - # After successful connection get_settings will be called to set up the battery. - # Set the current limits, populate cell count, etc. - # Return True if success, False for failure - - # BMS does not provide max charge-/discharge, so we have to use hardcoded/config values - self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT - self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT - - self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count - self.min_battery_voltage = utils.MIN_CELL_VOLTAGE * self.cell_count - - # init the cell array - for _ in range(self.cell_count): - self.cells.append(Cell(False)) - - return True - - def refresh_data(self): - # call all functions that will refresh the battery data. - # This will be called for every iteration (self.poll_interval) - # Return True if success, False for failure - result_status = self.read_status_data() - # sleep(0.5) - result_alarm = self.read_alarm_data() - - return result_status and result_alarm - - @staticmethod - def decode_alarm_byte(data_byte: int, alarm_bit: int, warn_bit: int): - if data_byte & (1 << alarm_bit) != 0: - return Protection.ALARM - if data_byte & (1 << warn_bit) != 0: - return Protection.WARNING - return Protection.OK - - def read_alarm_data(self): - data = self.read_serial_data_seplos( - self.encode_cmd(address=0x00, cid2=self.COMMAND_ALARM, info=b"01") - ) - # check if connection success - if data is False: - return False - - logger.debug("alarm info raw {}".format(data)) - return self.decode_alarm_data(bytes.fromhex(data.decode("ascii"))) - - def decode_alarm_data(self, data: bytes): - logger.debug("alarm info decoded {}".format(data)) - voltage_alarm_byte = data[30] - self.protection.voltage_cell_low = Seplos.decode_alarm_byte( - data_byte=voltage_alarm_byte, alarm_bit=3, warn_bit=2 - ) - # cell high voltage is actually unused because DBUS does not seem to support it, decoding anyway - # c.f. https://github.com/victronenergy/venus/wiki/dbus#battery - self.protection.voltage_cell_high = Seplos.decode_alarm_byte( - data_byte=voltage_alarm_byte, alarm_bit=1, warn_bit=0 - ) - self.protection.voltage_low = Seplos.decode_alarm_byte( - data_byte=voltage_alarm_byte, alarm_bit=7, warn_bit=6 - ) - self.protection.voltage_high = Seplos.decode_alarm_byte( - data_byte=voltage_alarm_byte, alarm_bit=5, warn_bit=4 - ) - - temperature_alarm_byte = data[31] - self.protection.temp_low_charge = Seplos.decode_alarm_byte( - data_byte=temperature_alarm_byte, alarm_bit=3, warn_bit=2 - ) - self.protection.temp_high_charge = Seplos.decode_alarm_byte( - data_byte=temperature_alarm_byte, alarm_bit=1, warn_bit=0 - ) - self.protection.temp_low_discharge = Seplos.decode_alarm_byte( - data_byte=temperature_alarm_byte, alarm_bit=7, warn_bit=6 - ) - self.protection.temp_high_discharge = Seplos.decode_alarm_byte( - data_byte=temperature_alarm_byte, alarm_bit=5, warn_bit=4 - ) - - current_alarm_byte = data[33] - self.protection.current_over = Seplos.decode_alarm_byte( - data_byte=current_alarm_byte, alarm_bit=1, warn_bit=0 - ) - self.protection.current_under = Seplos.decode_alarm_byte( - data_byte=current_alarm_byte, alarm_bit=3, warn_bit=2 - ) - - soc_alarm_byte = data[34] - self.protection.soc_low = Seplos.decode_alarm_byte( - data_byte=soc_alarm_byte, alarm_bit=3, warn_bit=2 - ) - - switch_byte = data[35] - self.discharge_fet = True if switch_byte & 0b01 != 0 else False - self.charge_fet = True if switch_byte & 0b10 != 0 else False - return True - - def read_status_data(self): - logger.debug("read status data") - data = self.read_serial_data_seplos( - self.encode_cmd(address=0x00, cid2=0x42, info=b"01") - ) - - # check if connection success - if data is False: - return False - - cell_count_offset = 4 - voltage_offset = 6 - temps_offset = 72 - self.cell_count = Seplos.int_from_1byte_hex_ascii( - data=data, offset=cell_count_offset - ) - if self.cell_count == len(self.cells): - for i in range(self.cell_count): - voltage = ( - Seplos.int_from_2byte_hex_ascii(data, voltage_offset + i * 4) / 1000 - ) - self.cells[i].voltage = voltage - logger.debug("Voltage cell[{}]={}V".format(i, voltage)) - for i in range(min(4, self.cell_count)): - temp = ( - Seplos.int_from_2byte_hex_ascii(data, temps_offset + i * 4) - 2731 - ) / 10 - self.cells[i].temp = temp - logger.debug("Temp cell[{}]={}°C".format(i, temp)) - - self.temp1 = ( - Seplos.int_from_2byte_hex_ascii(data, temps_offset + 4 * 4) - 2731 - ) / 10 - self.temp2 = ( - Seplos.int_from_2byte_hex_ascii(data, temps_offset + 5 * 4) - 2731 - ) / 10 - self.current = ( - Seplos.int_from_2byte_hex_ascii(data, offset=96, signed=True) / 100 - ) - self.voltage = Seplos.int_from_2byte_hex_ascii(data, offset=100) / 100 - self.capacity_remain = Seplos.int_from_2byte_hex_ascii(data, offset=104) / 100 - self.capacity = Seplos.int_from_2byte_hex_ascii(data, offset=110) / 100 - self.soc = Seplos.int_from_2byte_hex_ascii(data, offset=114) / 10 - self.cycles = Seplos.int_from_2byte_hex_ascii(data, offset=122) - self.hardware_version = "Seplos BMS {} cells".format(self.cell_count) - - logger.debug("Current = {}A , Voltage = {}V".format(self.current, self.voltage)) - logger.debug( - "Capacity = {}/{}Ah , SOC = {}%".format( - self.capacity_remain, self.capacity, self.soc - ) - ) - logger.debug("Cycles = {}".format(self.cycles)) - logger.debug( - "Environment temp = {}°C , Power temp = {}°C".format( - self.temp1, self.temp2 - ) - ) - logger.debug("HW:" + self.hardware_version) - - return True - - @staticmethod - def is_valid_frame(data: bytes) -> bool: - """checks if data contains a valid frame - * minimum length is 18 Byte - * checksum needs to be valid - * also checks for error code as return code in cid2 - * not checked: lchksum - """ - if len(data) < 18: - logger.warning("short read, data={}".format(data)) - return False - - chksum = Seplos.get_checksum(data[1:-5]) - if chksum != Seplos.int_from_2byte_hex_ascii(data, -5): - logger.warning("checksum error") - return False - - cid2 = data[7:9] - if cid2 != b"00": - logger.warning("command returned with error code {}".format(cid2)) - return False - - return True - - def read_serial_data_seplos(self, command): - logger.debug("read serial data seplos") - - with serial.Serial(self.port, baudrate=self.baud_rate, timeout=1) as ser: - ser.flushOutput() - ser.flushInput() - written = ser.write(command) - logger.debug( - "wrote {} bytes to serial port {}, command={}".format( - written, self.port, command - ) - ) - - data = ser.readline() - - if not Seplos.is_valid_frame(data): - return False - - length_pos = 10 - return_data = data[length_pos + 3 : -5] - info_length = Seplos.int_from_2byte_hex_ascii(b"0" + data[length_pos:], 0) - logger.debug( - "return info data of length {} : {}".format(info_length, return_data) - ) - - return return_data From ff1b14300a461154aef8b8783dfae251a959113b Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 10:26:38 +0200 Subject: [PATCH 168/209] Cleanup --- etc/dbus-serialbattery/default_config.ini | 138 ---------------------- 1 file changed, 138 deletions(-) delete mode 100644 etc/dbus-serialbattery/default_config.ini diff --git a/etc/dbus-serialbattery/default_config.ini b/etc/dbus-serialbattery/default_config.ini deleted file mode 100644 index b9d96465..00000000 --- a/etc/dbus-serialbattery/default_config.ini +++ /dev/null @@ -1,138 +0,0 @@ -[DEFAULT] -LINEAR_LIMITATION_ENABLE = False - -; battery Current limits -MAX_BATTERY_CHARGE_CURRENT = 70.0 -MAX_BATTERY_DISCHARGE_CURRENT = 90.0 - -; -------- Cell Voltage limitation --------- -; Description: -; Maximal charge / discharge current will be in-/decreased depending on min- and max-cell-voltages -; Example: 18cells * 3.55V/cell = 63.9V max charge voltage. 18 * 2.7V = 48,6V min discharge voltage -; ... but the (dis)charge current will be (in-/)decreased, if even ONE SINGLE BATTERY CELL reaches the limits - -; Charge current control management referring to cell-voltage enable (True/False). -CCCM_CV_ENABLE = True -; Discharge current control management referring to cell-voltage enable (True/False). -DCCM_CV_ENABLE = True - -; Set Steps to reduce battery current. The current will be changed linear between those steps -CELL_VOLTAGES_WHILE_CHARGING = 3.55,3.50,3.45,3.30 -MAX_CHARGE_CURRENT_CV_FRACTION = 0,0.05,0.5,1 - -CELL_VOLTAGES_WHILE_DISCHARGING = 2.70,2.80,2.90,3.10 -MAX_DISCHARGE_CURRENT_CV_FRACTION = 0,0.1,0.5,1 - -; -------- Temperature limitation --------- -; Description: -; Maximal charge / discharge current will be in-/decreased depending on temperature -; Example: The temperature limit will be monitored to control the currents. If there are two temperature senors, -; then the worst case will be calculated and the more secure lower current will be set. -; Charge current control management referring to temperature enable (True/False). -CCCM_T_ENABLE = True -; Charge current control management referring to temperature enable (True/False). -DCCM_T_ENABLE = True - -; Set Steps to reduce battery current. The current will be changed linear between those steps -TEMPERATURE_LIMITS_WHILE_CHARGING = 0,2,5,10,15,20,35,40,55 -MAX_CHARGE_CURRENT_T_FRACTION = 0,0.1,0.2,0.4,0.8,1,1,0.4,0 - -TEMPERATURE_LIMITS_WHILE_DISCHARGING = -20,0,5,10,15,45,55 -MAX_DISCHARGE_CURRENT_T_FRACTION = 0,.2,.3,.4,1,1,0 - -; if the cell voltage reaches 3.55V, then reduce current battery-voltage by 0.01V -; if the cell voltage goes over 3.6V, then the maximum penalty will not be exceeded -; there will be a sum of all penalties for each cell, which exceeds the limits -PENALTY_AT_CELL_VOLTAGE = 3.45,3.55,3.6 -; this voltage will be subtracted -PENALTY_BATTERY_VOLTAGE = 0.01,1.0,2.0 - - -; -------- SOC limitation --------- -; Description: -; Maximal charge / discharge current will be increased / decreased depending on State of Charge, see CC_SOC_LIMIT1 etc. -; The State of Charge (SoC) charge / discharge current will be in-/decreased depending on SOC. -; Example: 16cells * 3.45V/cell = 55,2V max charge voltage. 16*2.9V = 46,4V min discharge voltage -; Cell min/max voltages - used with the cell count to get the min/max battery voltage -MIN_CELL_VOLTAGE = 2.9 -MAX_CELL_VOLTAGE = 3.45 -FLOAT_CELL_VOLTAGE = 3.35 -MAX_VOLTAGE_TIME_SEC = 900 -SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = 90 - -; Charge current control management enable (True/False). -CCCM_SOC_ENABLE = True -; Discharge current control management enable (True/False). -DCCM_SOC_ENABLE = True - -; charge current soc limits -CC_SOC_LIMIT1 = 98 -CC_SOC_LIMIT2 = 95 -CC_SOC_LIMIT3 = 91 - -; charge current limits -CC_CURRENT_LIMIT1_FRACTION = 0.1 -CC_CURRENT_LIMIT2_FRACTION = 0.3 -CC_CURRENT_LIMIT3_FRACTION = 0.5 - -; discharge current soc limits -DC_SOC_LIMIT1 = 10 -DC_SOC_LIMIT2 = 20 -DC_SOC_LIMIT3 = 30 - -; discharge current limits -DC_CURRENT_LIMIT1_FRACTION = 0.1 -DC_CURRENT_LIMIT2_FRACTION = 0.3 -DC_CURRENT_LIMIT3_FRACTION = 0.5 - -; Charge voltage control management enable (True/False). -CVCM_ENABLE = False - -; Simulate Midpoint graph (True/False). -MIDPOINT_ENABLE = False - -; soc low levels -SOC_LOW_WARNING = 20 -SOC_LOW_ALARM = 10 - -; Daly settings -; Battery capacity (amps) if the BMS does not support reading it -BATTERY_CAPACITY = 50 -; Invert Battery Current. Default non-inverted. Set to -1 to invert -INVERT_CURRENT_MEASUREMENT = 1 - -; TIME TO SOC settings [Valid values 0-100, but I don't recommend more that 20 intervals] -; Set of SoC percentages to report on dbus. The more you specify the more it will impact system performance. -; TIME_TO_SOC_POINTS = [100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0] -; Every 5% SoC -; TIME_TO_SOC_POINTS = [100, 95, 90, 85, 75, 50, 25, 20, 10, 0] -; No data set to disable -TIME_TO_SOC_POINTS = -; Specify TimeToSoc value type: [Valid values 1,2,3] -; TIME_TO_SOC_VALUE_TYPE = 1 ; Seconds -; TIME_TO_SOC_VALUE_TYPE = 2 ; Time string HH:MN:SC -; Both Seconds and time str " [days, HR:MN:SC]" -TIME_TO_SOC_VALUE_TYPE = 3 -; Specify how many loop cycles between each TimeToSoc updates -TIME_TO_SOC_LOOP_CYCLES = 5 -; Include TimeToSoC points when moving away from the SoC point. [Valid values True,False] -; These will be as negative time. Disabling this improves performance slightly. -TIME_TO_SOC_INC_FROM = False - - -; Select the format of cell data presented on dbus. [Valid values 0,1,2,3] -; 0 Do not publish all the cells (only the min/max cell data as used by the default GX) -; 1 Format: /Voltages/Cell; (also available for display on Remote Console) -; 2 Format: /Cell/#/Volts -; 3 Both formats 1 and 2 -BATTERY_CELL_DATA_FORMAT = 1 - -; Settings for ESC GreenMeter and Lipro devices -GREENMETER_ADDRESS = 1 -LIPRO_START_ADDRESS = 2 -LIPRO_END_ADDRESS = 4 -LIPRO_CELL_COUNT = 15 - -PUBLISH_CONFIG_VALUES = 1 - -BMS_TYPE = \ No newline at end of file From 12ec57ea35f311056dc3d8fe4421192cf1b5b8f4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 10:32:37 +0200 Subject: [PATCH 169/209] MOSFET temperature was displayed twice after merge --- etc/dbus-serialbattery/qml/PageBattery.qml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/etc/dbus-serialbattery/qml/PageBattery.qml b/etc/dbus-serialbattery/qml/PageBattery.qml index f1c74c5a..286ce74c 100644 --- a/etc/dbus-serialbattery/qml/PageBattery.qml +++ b/etc/dbus-serialbattery/qml/PageBattery.qml @@ -118,15 +118,6 @@ MbPage { displayUnit: user.temperatureUnit } } - - MbItemValue { - description: qsTr("MOSFET temperature") - show: item.valid - item { - bind: service.path("/System/MOSTemperature") - displayUnit: user.temperatureUnit - } - } MbItemValue { description: qsTr("MOSFET temperature") From 84556e8b2c756baac6bb655819e48f0ce63baf7c Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 10:44:50 +0200 Subject: [PATCH 170/209] updated changelog --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ea31c2..04d78dac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,12 @@ ## v1.0.0-jkbms_ble +### ATTENTION: Breaking changes! The config is now done in the `config.ini`. All values from the `utils.py` gets lost. The changes in the `config.ini` will persists future updates. + +* Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel * Added: Balancing status for JKBMS by @mr-manuel * Added: Balancing switch status for JKBMS by @mr-manuel * Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel -* Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel * Added: Charge Mode display by @mr-manuel * Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel * Added: Config file by @ppuetsch @@ -25,8 +27,8 @@ * Added: JKBMS BLE - MOS temperature by @mr-manuel * Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel * Added: Post install notes by @mr-manuel -* Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel * Added: Read charge/discharge limits from JKBMS by @mr-manuel +* Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel * Added: Script to install directly from repository by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel From 56fecf3125bd7bdec195bc6c7f3ce6c1ac24e2bb Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 3 May 2023 15:25:43 +0200 Subject: [PATCH 171/209] Small fixes --- etc/dbus-serialbattery/bms/lltjbd.py | 2 +- etc/dbus-serialbattery/reinstall-local.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 8b34c920..a74697cc 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -158,7 +158,7 @@ def read_gen_data(self): ) = unpack_from(">HhHHHHhHHBBBBB", gen_data) self.voltage = voltage / 100 self.current = current / 100 - self.soc = round(100 * capacity_remain / capacity, 0) + self.soc = round(100 * capacity_remain / capacity, 2) self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) diff --git a/etc/dbus-serialbattery/reinstall-local.sh b/etc/dbus-serialbattery/reinstall-local.sh index aba7436a..c9ee661d 100755 --- a/etc/dbus-serialbattery/reinstall-local.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -63,7 +63,7 @@ fi ### BLUETOOTH PART | START ### # get BMS list from config file -bluetooth_bms=$(awk -F "=" '/BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) +bluetooth_bms=$(awk -F "=" '/^BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) #echo $bluetooth_bms # clear whitespaces From bbb7b1bfb26c9dc1afb17bb51ca085585b0cc586 Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Wed, 3 May 2023 20:23:32 +0000 Subject: [PATCH 172/209] fix disconnection behaviour: on disconnect, show '---' after 10s and 'not connected' after 60s --- etc/dbus-serialbattery/battery.py | 121 ++++++++++++++++++--------- etc/dbus-serialbattery/dbushelper.py | 25 ++++-- 2 files changed, 100 insertions(+), 46 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 6b40846a..68e6e77b 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -62,8 +62,15 @@ def __init__(self, port, baud, address): self.type = "Generic" self.poll_interval = 1000 self.online = True - self.hardware_version = None + self.cell_count = None + + self.init_values() + + # used to identify a BMS when multiple BMS are connected - planned for future use + self.unique_identifier = None + + def init_values(self): self.voltage = None self.current = None self.capacity_remain = None @@ -78,7 +85,6 @@ def __init__(self, port, baud, address): self.charge_fet = None self.discharge_fet = None self.balance_fet = None - self.cell_count = None self.temp_sensors = None self.temp1 = None self.temp2 = None @@ -103,9 +109,6 @@ def __init__(self, port, baud, address): self.max_battery_charge_current = None self.max_battery_discharge_current = None - # used to identify a BMS when multiple BMS are connected - planned for future use - self.unique_identifier = None - @abstractmethod def test_connection(self) -> bool: """ @@ -306,12 +309,20 @@ def manage_charge_current(self) -> None: # Manage Charge Current Limitations charge_limits = [ self.max_battery_charge_current + if self.max_battery_charge_current is not None + else 0 ] # gets removed after finished testing - charge_limits_new = {self.max_battery_charge_current: "None (Max Config Limit)"} + charge_limits_new = { + self.max_battery_charge_current + if self.max_battery_charge_current is not None + else 0: "None (Max Config Limit)" + } if utils.CCCM_CV_ENABLE: tmp = self.calcMaxChargeCurrentReferringToCellVoltage() - charge_limits.append(tmp) # gets removed after finished testing + charge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_charge_current: " # + str(self.max_battery_charge_current) @@ -327,7 +338,9 @@ def manage_charge_current(self) -> None: if utils.CCCM_T_ENABLE: tmp = self.calcMaxChargeCurrentReferringToTemperature() - charge_limits.append(tmp) # gets removed after finished testing + charge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_charge_current: " # + str(self.max_battery_charge_current) @@ -341,7 +354,9 @@ def manage_charge_current(self) -> None: if utils.CCCM_SOC_ENABLE: tmp = self.calcMaxChargeCurrentReferringToSoc() - charge_limits.append(tmp) # gets removed after finished testing + charge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_charge_current: " # + str(self.max_battery_charge_current) @@ -372,14 +387,20 @@ def manage_charge_current(self) -> None: # Manage Discharge Current Limitations discharge_limits = [ self.max_battery_discharge_current + if self.max_battery_discharge_current is not None + else 0 ] # gets removed after finished testing discharge_limits_new = { - self.max_battery_discharge_current: "None (Max Config Limit)" + self.max_battery_discharge_current + if self.max_battery_discharge_current is not None + else 0: "None (Max Config Limit)" } if utils.DCCM_CV_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToCellVoltage() - discharge_limits.append(tmp) # gets removed after finished testing + discharge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_discharge_current: " # + str(self.max_battery_discharge_current) @@ -395,7 +416,9 @@ def manage_charge_current(self) -> None: if utils.DCCM_T_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToTemperature() - discharge_limits.append(tmp) # gets removed after finished testing + discharge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_discharge_current: " # + str(self.max_battery_discharge_current) @@ -411,7 +434,9 @@ def manage_charge_current(self) -> None: if utils.DCCM_SOC_ENABLE: tmp = self.calcMaxDischargeCurrentReferringToSoc() - discharge_limits.append(tmp) # gets removed after finished testing + discharge_limits.append( + tmp if tmp is not None else 0 + ) # gets removed after finished testing # logging.error("self.max_battery_discharge_current: " # + str(self.max_battery_discharge_current) @@ -772,43 +797,61 @@ def extract_from_temp_values(self, extractor) -> Union[float, None]: return None def get_temp(self) -> Union[float, None]: - if utils.TEMP_BATTERY == 1: - return self.temp1 - elif utils.TEMP_BATTERY == 2: - return self.temp2 - else: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: round( - (float(temp1) + float(temp2)) / 2, 2 + try: + if utils.TEMP_BATTERY == 1: + return self.temp1 + elif utils.TEMP_BATTERY == 2: + return self.temp2 + else: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: round( + (float(temp1) + float(temp2)) / 2, 2 + ) ) - ) + except: + return None def get_min_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: min(temp1, temp2) - ) + try: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: min(temp1, temp2) + ) + except: + return None def get_min_temp_id(self) -> Union[str, None]: - if self.temp1 < self.temp2: - return utils.TEMP_1_NAME - else: - return utils.TEMP_2_NAME + try: + if self.temp1 < self.temp2: + return utils.TEMP_1_NAME + else: + return utils.TEMP_2_NAME + except: + return None def get_max_temp(self) -> Union[float, None]: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: max(temp1, temp2) - ) + try: + return self.extract_from_temp_values( + extractor=lambda temp1, temp2: max(temp1, temp2) + ) + except: + return None def get_max_temp_id(self) -> Union[str, None]: - if self.temp1 > self.temp2: - return utils.TEMP_1_NAME - else: - return utils.TEMP_2_NAME + try: + if self.temp1 > self.temp2: + return utils.TEMP_1_NAME + else: + return utils.TEMP_2_NAME + except: + return None def get_mos_temp(self) -> Union[float, None]: - if self.temp_mos is not None: - return self.temp_mos - else: + try: + if self.temp_mos is not None: + return self.temp_mos + else: + return None + except: return None def log_cell_data(self) -> bool: diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 246a1aad..578ab782 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -332,6 +332,8 @@ def publish_battery(self, loop): # If the battery is offline for more than 10 polls (polled every second for most batteries) if self.error_count >= 10: self.battery.online = False + self.battery.init_values() + # Has it completely failed if self.error_count >= 60: loop.quit() @@ -352,11 +354,19 @@ def publish_battery(self, loop): def publish_dbus(self): # Update SOC, DC and System items self._dbusservice["/System/NrOfCellsPerBattery"] = self.battery.cell_count - self._dbusservice["/Soc"] = round(self.battery.soc, 2) - self._dbusservice["/Dc/0/Voltage"] = round(self.battery.voltage, 2) - self._dbusservice["/Dc/0/Current"] = round(self.battery.current, 2) - self._dbusservice["/Dc/0/Power"] = round( - self.battery.voltage * self.battery.current, 2 + self._dbusservice["/Soc"] = ( + round(self.battery.soc, 2) if self.battery.soc is not None else None + ) + self._dbusservice["/Dc/0/Voltage"] = ( + round(self.battery.voltage, 2) if self.battery.voltage is not None else None + ) + self._dbusservice["/Dc/0/Current"] = ( + round(self.battery.current, 2) if self.battery.current is not None else None + ) + self._dbusservice["/Dc/0/Power"] = ( + round(self.battery.voltage * self.battery.current, 2) + if self.battery.current is not None and self.battery.current is not None + else None ) self._dbusservice["/Dc/0/Temperature"] = self.battery.get_temp() self._dbusservice["/Capacity"] = self.battery.get_capacity_remain() @@ -540,5 +550,6 @@ def publish_dbus(self): except: pass - logger.debug("logged to dbus [%s]" % str(round(self.battery.soc, 2))) - self.battery.log_cell_data() + if self.battery.soc is not None: + logger.debug("logged to dbus [%s]" % str(round(self.battery.soc, 2))) + self.battery.log_cell_data() From d647b1e62593da9145df1895897852b9217998ee Mon Sep 17 00:00:00 2001 From: Bernd Stahlbock Date: Wed, 3 May 2023 21:13:35 +0000 Subject: [PATCH 173/209] fix flake errors --- etc/dbus-serialbattery/battery.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index cf269c96..44275371 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -798,7 +798,7 @@ def get_temp(self) -> Union[float, None]: (float(temp1) + float(temp2)) / 2, 2 ) ) - except: + except TypeError: return None def get_min_temp(self) -> Union[float, None]: @@ -806,7 +806,7 @@ def get_min_temp(self) -> Union[float, None]: return self.extract_from_temp_values( extractor=lambda temp1, temp2: min(temp1, temp2) ) - except: + except TypeError: return None def get_min_temp_id(self) -> Union[str, None]: @@ -815,7 +815,7 @@ def get_min_temp_id(self) -> Union[str, None]: return utils.TEMP_1_NAME else: return utils.TEMP_2_NAME - except: + except TypeError: return None def get_max_temp(self) -> Union[float, None]: @@ -823,7 +823,7 @@ def get_max_temp(self) -> Union[float, None]: return self.extract_from_temp_values( extractor=lambda temp1, temp2: max(temp1, temp2) ) - except: + except TypeError: return None def get_max_temp_id(self) -> Union[str, None]: @@ -832,16 +832,13 @@ def get_max_temp_id(self) -> Union[str, None]: return utils.TEMP_1_NAME else: return utils.TEMP_2_NAME - except: + except TypeError: return None def get_mos_temp(self) -> Union[float, None]: - try: - if self.temp_mos is not None: - return self.temp_mos - else: - return None - except: + if self.temp_mos is not None: + return self.temp_mos + else: return None def log_cell_data(self) -> bool: From 8ed42ae7d6726d09ec6f1f85d2434f3a7a52c967 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 08:31:51 +0200 Subject: [PATCH 174/209] small fix --- CHANGELOG.md | 1 + etc/dbus-serialbattery/restart-driver.sh | 2 +- etc/dbus-serialbattery/utils.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04d78dac..f32c1dc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ * Added: Post install notes by @mr-manuel * Added: Read charge/discharge limits from JKBMS by @mr-manuel * Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel +* Added: Reset values to None, if battery goes offline (not reachable for 10s) by @transistorgit * Added: Script to install directly from repository by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel diff --git a/etc/dbus-serialbattery/restart-driver.sh b/etc/dbus-serialbattery/restart-driver.sh index 59b0b5cd..853a8b97 100755 --- a/etc/dbus-serialbattery/restart-driver.sh +++ b/etc/dbus-serialbattery/restart-driver.sh @@ -14,7 +14,7 @@ pkill -f "python .*/$DRIVERNAME.py" # get BMS list from config file -bluetooth_bms=$(awk -F "=" '/BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) +bluetooth_bms=$(awk -F "=" '/^BLUETOOTH_BMS/ {print $2}' /data/etc/dbus-serialbattery/config.ini) # clear whitespaces bluetooth_bms_clean="$(echo $bluetooth_bms | sed 's/\s*,\s*/,/g')" # split into array diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 74d6e58a..9a4e3fd0 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -36,7 +36,7 @@ def _get_list_from_config( # Constants - Need to dynamically get them in future DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0-jkbms_ble (20230503)" +DRIVER_SUBVERSION = ".0-jkbms_ble (20230504)" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 77fc2127f1272423394d54667ed6ee4b17fefe01 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 11:47:11 +0200 Subject: [PATCH 175/209] Added: apply max voltage if CVCM_ENABLE is False before float voltage was applied --- CHANGELOG.md | 7 ++--- etc/dbus-serialbattery/battery.py | 12 ++++++--- etc/dbus-serialbattery/config.default.ini | 33 ++++++++++++++--------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f32c1dc3..78d8f234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### ATTENTION: Breaking changes! The config is now done in the `config.ini`. All values from the `utils.py` gets lost. The changes in the `config.ini` will persists future updates. * Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel +* Added: Apply max voltage, if `CVCM_ENABLE` is `False`. Before float voltage was applied by @mr-manuel * Added: Balancing status for JKBMS by @mr-manuel * Added: Balancing switch status for JKBMS by @mr-manuel * Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel @@ -17,8 +18,8 @@ * Added: Driver uninstall script by @mr-manuel * Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel * Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel -* Added: Install needed components automatically after a Venus OS upgrade by @mr-manuel -* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @mr-manuel +* Added: Install needed Bluetooth components automatically after a Venus OS upgrade by @mr-manuel +* Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @baphomett * Added: JKBMS BLE - Balancing switch status by @mr-manuel * Added: JKBMS BLE - Capacity by @mr-manuel * Added: JKBMS BLE - Cell imbalance alert by @mr-manuel @@ -73,4 +74,4 @@ * Changed: Temperature alarm changed in order to not trigger all in the same condition for JKBMS by @mr-manuel * Changed: Time-To-Soc repetition from cycles to seconds. Minimum value is every 5 seconds. This prevents CPU overload and ensures system stability. Renamed `TIME_TO_SOC_LOOP_CYCLES` to `TIME_TO_SOC_RECALCULATE_EVERY` by @mr-manuel * Changed: Time-To-Soc string from `days, HR:MN:SC` to `d h m s` (same as Time-To-Go) by @mr-manuel -* Changed: Uninstall also installed Bluetooth modules on uninstall. by @mr-manuel +* Changed: Uninstall also installed Bluetooth modules on uninstall by @mr-manuel diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 44275371..c1f4686b 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -165,10 +165,15 @@ def manage_charge_voltage(self) -> None: manages the charge voltage by setting self.control_voltage :return: None """ - if utils.LINEAR_LIMITATION_ENABLE: - self.manage_charge_voltage_linear() + if utils.CVCM_ENABLE: + if utils.LINEAR_LIMITATION_ENABLE: + self.manage_charge_voltage_linear() + else: + self.manage_charge_voltage_step() + # on CVCM_ENABLE = False apply max voltage else: - self.manage_charge_voltage_step() + self.control_voltage = round((utils.MAX_CELL_VOLTAGE * self.cell_count), 3) + self.charge_mode = "Keep always max voltage" def manage_charge_voltage_linear(self) -> None: """ @@ -268,6 +273,7 @@ def manage_charge_voltage_linear(self) -> None: and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT ): self.charge_mode += " + Balancing" + self.charge_mode += " (Linear Mode)" def manage_charge_voltage_step(self) -> None: diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index f2ac2ab4..c1a356e9 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -20,9 +20,10 @@ BLUETOOTH_BMS = ; --------- Charge mode --------- ; Choose the mode for voltage / current limitations (True / False) -; False is a step mode. This is the default with limitations on hard boundary steps -; True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) -; For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded +; False is a step mode: This is the default with limitations on hard boundary steps +; True is a linear mode: +; For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) +; For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded LINEAR_LIMITATION_ENABLE = True ; Specify in seconds how often the linear values should be recalculated @@ -33,17 +34,23 @@ LINEAR_RECALCULATION_ON_PERC_CHANGE = 5 ; --------- Charge Voltage limitation (affecting CVL) --------- -; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) and back -; Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it -; switches back to max voltage. -; Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to -; float voltage after 300 (fixed) additional seconds. After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT -; it switches back to max voltage. -; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. -; Allow max voltage of 55.2V again, if SoC is once below 90% +; Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float +; voltage (FLOAT_CELL_VOLTAGE * cell count) and back +; False: Max charging voltage is always kept +; True: Max charging voltage is reduced based on charge mode +; Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After +; SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it switches back to max voltage. +; Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to +; CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to float voltage after 300 (fixed) +; additional seconds. After cell voltage difference is greater or equal to +; CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT it switches back to max voltage. +; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to +; float voltage of 53.6V to don't stress the batteries. Allow max voltage of 55.2V again, if SoC is +; once below 90% ; OR -; The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float voltage of 53.6V after 300 additional seconds -; to don't stress the batteries. Allow max voltage of 55.2V again if max cell difference is above 0.050V +; The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float +; voltage of 53.6V after 300 additional seconds to don't stress the batteries. Allow max voltage of +; 55.2V again if max cell difference is above 0.050V ; Charge voltage control management enable (True/False). CVCM_ENABLE = True From 9acfb1543402aea43dd9c4d7bc92825f9c7e4a26 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 12:20:47 +0200 Subject: [PATCH 176/209] fixed type error --- etc/dbus-serialbattery/battery.py | 263 ++++++++++++++++-------------- 1 file changed, 139 insertions(+), 124 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index c1f4686b..f1afcb16 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -184,97 +184,106 @@ def manage_charge_voltage_linear(self) -> None: voltageSum = 0 penaltySum = 0 tDiff = 0 - if utils.CVCM_ENABLE: - # calculate battery sum - for i in range(self.cell_count): - voltage = self.get_cell_voltage(i) - if voltage: - voltageSum += voltage - - # calculate penalty sum to prevent single cell overcharge by using current cell voltage - if voltage > utils.MAX_CELL_VOLTAGE: - # foundHighCellVoltage: reset to False is not needed, since it is recalculated every second - foundHighCellVoltage = True - penaltySum += voltage - utils.MAX_CELL_VOLTAGE - 0.010 - - voltageDiff = self.get_max_cell_voltage() - self.get_min_cell_voltage() - if self.max_voltage_start_time is None: + try: + if utils.CVCM_ENABLE: + # calculate battery sum + for i in range(self.cell_count): + voltage = self.get_cell_voltage(i) + if voltage: + voltageSum += voltage + + # calculate penalty sum to prevent single cell overcharge by using current cell voltage + if voltage > utils.MAX_CELL_VOLTAGE: + # foundHighCellVoltage: reset to False is not needed, since it is recalculated every second + foundHighCellVoltage = True + penaltySum += voltage - utils.MAX_CELL_VOLTAGE - 0.010 + + voltageDiff = self.get_max_cell_voltage() - self.get_min_cell_voltage() + + if self.max_voltage_start_time is None: + if ( + utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + and voltageDiff + <= utils.CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL + and self.allow_max_voltage + ): + self.max_voltage_start_time = time() + elif ( + # utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT + and not self.allow_max_voltage + ): + self.allow_max_voltage = True + else: + tDiff = time() - self.max_voltage_start_time + # if utils.MAX_VOLTAGE_TIME_SEC < tDiff: + # keep max voltage for 300 more seconds + if 300 < tDiff: + self.allow_max_voltage = False + self.max_voltage_start_time = None + + # INFO: battery will only switch to Absorption, if all cells are balanced. + # Reach MAX_CELL_VOLTAGE * cell count if they are all balanced. + if foundHighCellVoltage and self.allow_max_voltage: + # set CVL only once every LINEAR_RECALCULATION_EVERY seconds if ( - utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum - and voltageDiff <= utils.CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL - and self.allow_max_voltage + int(time()) - self.linear_cvl_last_set + >= utils.LINEAR_RECALCULATION_EVERY ): - self.max_voltage_start_time = time() - elif ( - # utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc - voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT - and not self.allow_max_voltage - ): - self.allow_max_voltage = True - else: - tDiff = time() - self.max_voltage_start_time - # if utils.MAX_VOLTAGE_TIME_SEC < tDiff: - # keep max voltage for 300 more seconds - if 300 < tDiff: - self.allow_max_voltage = False - self.max_voltage_start_time = None - - # INFO: battery will only switch to Absorption, if all cells are balanced. - # Reach MAX_CELL_VOLTAGE * cell count if they are all balanced. - if foundHighCellVoltage and self.allow_max_voltage: - # set CVL only once every LINEAR_RECALCULATION_EVERY seconds - if ( - int(time()) - self.linear_cvl_last_set - >= utils.LINEAR_RECALCULATION_EVERY - ): - self.linear_cvl_last_set = int(time()) + self.linear_cvl_last_set = int(time()) + + # Keep penalty above min battery voltage + self.control_voltage = round( + max( + voltageSum - penaltySum, + utils.MIN_CELL_VOLTAGE * self.cell_count, + ), + 3, + ) - # Keep penalty above min battery voltage - self.control_voltage = round( - max( - voltageSum - penaltySum, - utils.MIN_CELL_VOLTAGE * self.cell_count, - ), - 3, + self.charge_mode = ( + "Bulk dynamic" + # + " (vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" + if self.max_voltage_start_time is None + else "Absorption dynamic" + # + "(vS: " + # + str(round(voltageSum, 2)) + # + " - pS: " + # + str(round(penaltySum, 2)) + # + ")" ) - self.charge_mode = ( - "Bulk dynamic" - # + " (vS: " - # + str(round(voltageSum, 2)) - # + " - pS: " - # + str(round(penaltySum, 2)) - # + ")" - if self.max_voltage_start_time is None - else "Absorption dynamic" - # + "(vS: " - # + str(round(voltageSum, 2)) - # + " - pS: " - # + str(round(penaltySum, 2)) - # + ")" - ) + elif self.allow_max_voltage: + self.control_voltage = round( + (utils.MAX_CELL_VOLTAGE * self.cell_count), 3 + ) + self.charge_mode = ( + "Bulk" if self.max_voltage_start_time is None else "Absorption" + ) - elif self.allow_max_voltage: - self.control_voltage = round((utils.MAX_CELL_VOLTAGE * self.cell_count), 3) - self.charge_mode = ( - "Bulk" if self.max_voltage_start_time is None else "Absorption" - ) + else: + self.control_voltage = round( + (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3 + ) + self.charge_mode = "Float" - else: - self.control_voltage = round( - (utils.FLOAT_CELL_VOLTAGE * self.cell_count), 3 - ) - self.charge_mode = "Float" + if ( + self.allow_max_voltage + and self.get_balancing() + and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT + ): + self.charge_mode += " + Balancing" - if ( - self.allow_max_voltage - and self.get_balancing() - and voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT - ): - self.charge_mode += " + Balancing" + self.charge_mode += " (Linear Mode)" - self.charge_mode += " (Linear Mode)" + except TypeError: + self.control_voltage = None + self.charge_mode = "--" def manage_charge_voltage_step(self) -> None: """ @@ -283,55 +292,61 @@ def manage_charge_voltage_step(self) -> None: """ voltageSum = 0 tDiff = 0 - if utils.CVCM_ENABLE: - # calculate battery sum - for i in range(self.cell_count): - voltage = self.get_cell_voltage(i) - if voltage: - voltageSum += voltage - - if self.max_voltage_start_time is None: - # check if max voltage is reached and start timer to keep max voltage - if ( - utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum - and self.allow_max_voltage - ): - # example 2 - self.max_voltage_start_time = time() - - # check if reset soc is greater than battery soc - # this prevents flapping between max and float voltage - elif ( - utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc - and not self.allow_max_voltage - ): - self.allow_max_voltage = True - # do nothing + try: + if utils.CVCM_ENABLE: + # calculate battery sum + for i in range(self.cell_count): + voltage = self.get_cell_voltage(i) + if voltage: + voltageSum += voltage + + if self.max_voltage_start_time is None: + # check if max voltage is reached and start timer to keep max voltage + if ( + utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + and self.allow_max_voltage + ): + # example 2 + self.max_voltage_start_time = time() + + # check if reset soc is greater than battery soc + # this prevents flapping between max and float voltage + elif ( + utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + and not self.allow_max_voltage + ): + self.allow_max_voltage = True + + # do nothing + else: + pass + + # timer started else: - pass + tDiff = time() - self.max_voltage_start_time + if utils.MAX_VOLTAGE_TIME_SEC < tDiff: + self.allow_max_voltage = False + self.max_voltage_start_time = None + + else: + pass + + if self.allow_max_voltage: + self.control_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count + self.charge_mode = ( + "Bulk" if self.max_voltage_start_time is None else "Absorption" + ) - # timer started else: - tDiff = time() - self.max_voltage_start_time - if utils.MAX_VOLTAGE_TIME_SEC < tDiff: - self.allow_max_voltage = False - self.max_voltage_start_time = None + self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count + self.charge_mode = "Float" - else: - pass + self.charge_mode += " (Step Mode)" - if self.allow_max_voltage: - self.control_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count - self.charge_mode = ( - "Bulk" if self.max_voltage_start_time is None else "Absorption" - ) - - else: - self.control_voltage = utils.FLOAT_CELL_VOLTAGE * self.cell_count - self.charge_mode = "Float" - - self.charge_mode += " (Step Mode)" + except TypeError: + self.control_voltage = None + self.charge_mode = "--" def manage_charge_current(self) -> None: # Manage Charge Current Limitations From 7e1ada1e82c4eb4f9268f279561c2c48eba2ea9a Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 14:12:31 +0200 Subject: [PATCH 177/209] Added: BMS disconnect behaviour * Choose to block charge/discharge on disconnect * Trigger Venus OS alarm --- etc/dbus-serialbattery/config.default.ini | 8 +++++ etc/dbus-serialbattery/dbushelper.py | 36 +++++++++++++++++++---- etc/dbus-serialbattery/utils.py | 8 +++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index c1a356e9..8018e3bd 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -18,6 +18,14 @@ FLOAT_CELL_VOLTAGE = 3.375 ; Example with 3 BMS: Jkbms_Ble C8:47:8C:00:00:00, Jkbms_Ble C8:47:8C:00:00:11, Jkbms_Ble C8:47:8C:00:00:22 BLUETOOTH_BMS = +; --------- BMS disconnect behaviour --------- +; Description: Block charge and discharge when the communication to the BMS is lost. If you are removing the +; BMS on purpose, then you have to restart the driver/system to reset the block. +; False: Charge and discharge is not blocked on BMS communication loss +; True: Charge and discharge is blocked on BMS communication loss, it's unblocked when connection is established +; again or the driver/system is restarted +BLOCK_ON_DISCONNECT = False + ; --------- Charge mode --------- ; Choose the mode for voltage / current limitations (True / False) ; False is a step mode: This is the default with limitations on hard boundary steps diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 4f1b4a5f..77a48544 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -34,6 +34,7 @@ def __init__(self, battery): self.instance = 1 self.settings = None self.error_count = 0 + self.block_because_disconnect = False self._dbusservice = VeDbusService( "com.victronenergy.battery." + self.battery.port[self.battery.port.rfind("/") + 1 :], @@ -264,6 +265,7 @@ def setup_vedbus(self): self._dbusservice.add_path("/Alarms/LowChargeTemperature", None, writeable=True) self._dbusservice.add_path("/Alarms/HighTemperature", None, writeable=True) self._dbusservice.add_path("/Alarms/LowTemperature", None, writeable=True) + self._dbusservice.add_path("/Alarms/BmsCable", None, writeable=True) self._dbusservice.add_path( "/Alarms/HighInternalTemperature", None, writeable=True ) @@ -327,6 +329,11 @@ def publish_battery(self, loop): if success: self.error_count = 0 self.battery.online = True + + # unblock charge/discharge, if it was blocked when battery went offline + if utils.BLOCK_ON_DISCONNECT: + self.block_because_disconnect = False + else: self.error_count += 1 # If the battery is offline for more than 10 polls (polled every second for most batteries) @@ -334,6 +341,10 @@ def publish_battery(self, loop): self.battery.online = False self.battery.init_values() + # block charge/discharge + if utils.BLOCK_ON_DISCONNECT: + self.block_because_disconnect = True + # Has it completely failed if self.error_count >= 60: loop.quit() @@ -386,22 +397,34 @@ def publish_dbus(self): self._dbusservice["/History/ChargeCycles"] = self.battery.cycles self._dbusservice["/History/TotalAhDrawn"] = self.battery.total_ah_drawn self._dbusservice["/Io/AllowToCharge"] = ( - 1 if self.battery.charge_fet and self.battery.control_allow_charge else 0 + 1 + if self.battery.charge_fet + and self.battery.control_allow_charge + and self.block_because_disconnect is False + else 0 ) self._dbusservice["/Io/AllowToDischarge"] = ( 1 - if self.battery.discharge_fet and self.battery.control_allow_discharge + if self.battery.discharge_fet + and self.battery.control_allow_discharge + and self.block_because_disconnect is False else 0 ) self._dbusservice["/Io/AllowToBalance"] = 1 if self.battery.balance_fet else 0 self._dbusservice["/System/NrOfModulesBlockingCharge"] = ( 0 - if self.battery.charge_fet is None - or (self.battery.charge_fet and self.battery.control_allow_charge) + if ( + self.battery.charge_fet is None + or (self.battery.charge_fet and self.battery.control_allow_charge) + ) + and self.block_because_disconnect is False else 1 ) self._dbusservice["/System/NrOfModulesBlockingDischarge"] = ( - 0 if self.battery.discharge_fet is None or self.battery.discharge_fet else 1 + 0 + if (self.battery.discharge_fet is None or self.battery.discharge_fet) + and self.block_because_disconnect is False + else 1 ) self._dbusservice["/System/NrOfModulesOnline"] = 1 if self.battery.online else 0 self._dbusservice["/System/NrOfModulesOffline"] = ( @@ -477,6 +500,9 @@ def publish_dbus(self): self._dbusservice[ "/Alarms/LowTemperature" ] = self.battery.protection.temp_low_discharge + self._dbusservice["/Alarms/BmsCable"] = ( + 2 if self.block_because_disconnect else 0 + ) self._dbusservice[ "/Alarms/HighInternalTemperature" ] = self.battery.protection.temp_high_internal diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 9a4e3fd0..e5e8475f 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -54,6 +54,14 @@ def _get_list_from_config( # Max voltage can seen as absorption voltage FLOAT_CELL_VOLTAGE = float(config["DEFAULT"]["FLOAT_CELL_VOLTAGE"]) +# --------- BMS disconnect behaviour --------- +# Description: Block charge and discharge when the communication to the BMS is lost. If you are removing the +# BMS on purpose, then you have to restart the driver/system to reset the block. +# False: Charge and discharge is not blocked on BMS communication loss +# True: Charge and discharge is blocked on BMS communication loss, it's unblocked when connection is established +# again or the driver/system is restarted +BLOCK_ON_DISCONNECT = "True" == config["DEFAULT"]["BLOCK_ON_DISCONNECT"] + # --------- Charge mode --------- # Choose the mode for voltage / current limitations (True / False) # False is a step mode. This is the default with limitations on hard boundary steps From 2b32e11c11d4b524c36a38a35482c4360278a539 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 14:53:46 +0200 Subject: [PATCH 178/209] Changed: Remove wildcard import from dbushelper.py --- .flake8 | 2 +- etc/dbus-serialbattery/dbushelper.py | 56 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/.flake8 b/.flake8 index 0e731225..e7900ff1 100644 --- a/.flake8 +++ b/.flake8 @@ -8,7 +8,7 @@ exclude = ./etc/dbus-serialbattery/bms/mnb_utils_max17853.py, ./etc/dbus-serialbattery/bms/revov.py, #./etc/dbus-serialbattery/dbus-serialbattery.py, - ./etc/dbus-serialbattery/dbushelper.py, + #./etc/dbus-serialbattery/dbushelper.py, ./etc/dbus-serialbattery/minimalmodbus.py, ./velib_python venv diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 77a48544..8c1c9d5f 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -14,10 +14,10 @@ "/opt/victronenergy/dbus-systemcalc-py/ext/velib_python", ), ) -from vedbus import VeDbusService -from settingsdevice import SettingsDevice -import battery -from utils import * +from vedbus import VeDbusService # noqa: E402 +from settingsdevice import SettingsDevice # noqa: E402 +from utils import logger, publish_config_variables # noqa: E402 +import utils # noqa: E402 def get_bus(): @@ -122,7 +122,7 @@ def setup_vedbus(self): "/ProductName", "SerialBattery(" + self.battery.type + ")" ) self._dbusservice.add_path( - "/FirmwareVersion", str(DRIVER_VERSION) + DRIVER_SUBVERSION + "/FirmwareVersion", str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION ) self._dbusservice.add_path("/HardwareVersion", self.battery.hardware_version) self._dbusservice.add_path("/Connected", 1) @@ -271,11 +271,11 @@ def setup_vedbus(self): ) # cell voltages - if BATTERY_CELL_DATA_FORMAT > 0: + if utils.BATTERY_CELL_DATA_FORMAT > 0: for i in range(1, self.battery.cell_count + 1): cellpath = ( "/Cell/%s/Volts" - if (BATTERY_CELL_DATA_FORMAT & 2) + if (utils.BATTERY_CELL_DATA_FORMAT & 2) else "/Voltages/Cell%s" ) self._dbusservice.add_path( @@ -284,11 +284,11 @@ def setup_vedbus(self): writeable=True, gettextcallback=lambda p, v: "{:0.3f}V".format(v), ) - if BATTERY_CELL_DATA_FORMAT & 1: + if utils.BATTERY_CELL_DATA_FORMAT & 1: self._dbusservice.add_path( "/Balances/Cell%s" % (str(i)), None, writeable=True ) - pathbase = "Cell" if (BATTERY_CELL_DATA_FORMAT & 2) else "Voltages" + pathbase = "Cell" if (utils.BATTERY_CELL_DATA_FORMAT & 2) else "Voltages" self._dbusservice.add_path( "/%s/Sum" % pathbase, None, @@ -305,18 +305,18 @@ def setup_vedbus(self): # Create TimeToSoC items only if enabled if self.battery.capacity is not None: # Create TimeToGo item - if TIME_TO_GO_ENABLE: + if utils.TIME_TO_GO_ENABLE: self._dbusservice.add_path("/TimeToGo", None, writeable=True) # Create TimeToSoc items - if len(TIME_TO_SOC_POINTS) > 0: - for num in TIME_TO_SOC_POINTS: + if len(utils.TIME_TO_SOC_POINTS) > 0: + for num in utils.TIME_TO_SOC_POINTS: self._dbusservice.add_path( "/TimeToSoC/" + str(num), None, writeable=True ) - logger.info(f"publish config values = {PUBLISH_CONFIG_VALUES}") - if PUBLISH_CONFIG_VALUES == 1: + logger.info(f"publish config values = {utils.PUBLISH_CONFIG_VALUES}") + if utils.PUBLISH_CONFIG_VALUES == 1: publish_config_variables(self._dbusservice) return True @@ -358,7 +358,7 @@ def publish_battery(self, loop): # publish all the data from the battery object to dbus self.publish_dbus() - except: + except Exception: traceback.print_exc() loop.quit() @@ -508,40 +508,42 @@ def publish_dbus(self): ] = self.battery.protection.temp_high_internal # cell voltages - if BATTERY_CELL_DATA_FORMAT > 0: + if utils.BATTERY_CELL_DATA_FORMAT > 0: try: voltageSum = 0 for i in range(self.battery.cell_count): voltage = self.battery.get_cell_voltage(i) cellpath = ( "/Cell/%s/Volts" - if (BATTERY_CELL_DATA_FORMAT & 2) + if (utils.BATTERY_CELL_DATA_FORMAT & 2) else "/Voltages/Cell%s" ) self._dbusservice[cellpath % (str(i + 1))] = voltage - if BATTERY_CELL_DATA_FORMAT & 1: + if utils.BATTERY_CELL_DATA_FORMAT & 1: self._dbusservice[ "/Balances/Cell%s" % (str(i + 1)) ] = self.battery.get_cell_balancing(i) if voltage: voltageSum += voltage - pathbase = "Cell" if (BATTERY_CELL_DATA_FORMAT & 2) else "Voltages" + pathbase = ( + "Cell" if (utils.BATTERY_CELL_DATA_FORMAT & 2) else "Voltages" + ) self._dbusservice["/%s/Sum" % pathbase] = voltageSum self._dbusservice["/%s/Diff" % pathbase] = ( self.battery.get_max_cell_voltage() - self.battery.get_min_cell_voltage() ) - except: + except Exception: pass # Update TimeToGo and/or TimeToSoC try: if ( self.battery.capacity is not None - and (TIME_TO_GO_ENABLE or len(TIME_TO_SOC_POINTS) > 0) + and (utils.TIME_TO_GO_ENABLE or len(utils.TIME_TO_SOC_POINTS) > 0) and ( int(time()) - self.battery.time_to_soc_update - >= TIME_TO_SOC_RECALCULATE_EVERY + >= utils.TIME_TO_SOC_RECALCULATE_EVERY ) ): self.battery.time_to_soc_update = int(time()) @@ -550,13 +552,13 @@ def publish_dbus(self): ) # Update TimeToGo item - if TIME_TO_GO_ENABLE: + if utils.TIME_TO_GO_ENABLE: # Update TimeToGo item, has to be a positive int since it's used from dbus-systemcalc-py self._dbusservice["/TimeToGo"] = ( abs( int( self.battery.get_timeToSoc( - SOC_LOW_WARNING, crntPrctPerSec, True + utils.SOC_LOW_WARNING, crntPrctPerSec, True ) ) ) @@ -565,15 +567,15 @@ def publish_dbus(self): ) # Update TimeToSoc items - if len(TIME_TO_SOC_POINTS) > 0: - for num in TIME_TO_SOC_POINTS: + if len(utils.TIME_TO_SOC_POINTS) > 0: + for num in utils.TIME_TO_SOC_POINTS: self._dbusservice["/TimeToSoC/" + str(num)] = ( self.battery.get_timeToSoc(num, crntPrctPerSec) if self.battery.current else None ) - except: + except Exception: pass if self.battery.soc is not None: From cd8c22a431d702710423302050f877c7e1601e3f Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 4 May 2023 15:48:14 +0200 Subject: [PATCH 179/209] small fixes --- CHANGELOG.md | 6 ++++-- etc/dbus-serialbattery/bms/jkbms_ble.py | 6 ++++-- etc/dbus-serialbattery/reinstall-local.sh | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d8f234..e6a84661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,18 @@ ### ATTENTION: Breaking changes! The config is now done in the `config.ini`. All values from the `utils.py` gets lost. The changes in the `config.ini` will persists future updates. * Added: `self.unique_identifier` to the battery class. Used to identify a BMS when multiple BMS are connected - planned for future use by @mr-manuel +* Added: Alert is triggered, when BMS communication is lost by @mr-manuel * Added: Apply max voltage, if `CVCM_ENABLE` is `False`. Before float voltage was applied by @mr-manuel * Added: Balancing status for JKBMS by @mr-manuel * Added: Balancing switch status for JKBMS by @mr-manuel * Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel +* Added: Block charge/discharge when BMS communication is lost. Can be enabled trough the config file by @mr-manuel * Added: Charge Mode display by @mr-manuel * Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel * Added: Config file by @ppuetsch * Added: Create empty `config.ini` for easier user usage by @mr-manuel * Added: Cronjob to restart Bluetooth service every 12 hours by @mr-manuel -* Added: Daly BMS read capacity https://github.com/Louisvdw/dbus-serialbattery/pull/594 by transistorgit +* Added: Daly BMS read capacity https://github.com/Louisvdw/dbus-serialbattery/pull/594 by @transistorgit * Added: Driver uninstall script by @mr-manuel * Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel * Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel @@ -66,7 +68,7 @@ * Changed: Moved Bluetooth part to `reinstall-local.sh` by @mr-manuel * Changed: Moved BMS scripts to subfolder by @mr-manuel * Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel -* Changed: Removed wildcard imports from several BMS drivers and fixed black lint errors by @mr-manuel +* Changed: Removed all wildcard imports and fixed black lint errors by @mr-manuel * Changed: Renamed scripts for better reading #532 by @mr-manuel * Changed: Reworked and optimized installation scripts by @mr-manuel * Changed: Separate Time-To-Go and Time-To-SoC activation by @mr-manuel diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py index b495aa8e..6ba2f631 100644 --- a/etc/dbus-serialbattery/bms/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -225,8 +225,10 @@ def reset_bluetooth(self): # if self.jk.is_running(): # self.jk.stop_scraping() logger.info("scraping ended, issuing sys-commands") - os.system("kill -9 $(pidof bluetoothd)") - # os.system("/etc/init.d/bluetooth stop") is not enugh, kill -9 via pid is needed + # process kill is needed, since the service/bluetooth driver is probably freezed + os.system('pkill -f "bluetoothd"') + # stop will not work, if service/bluetooth driver is stuck + # os.system("/etc/init.d/bluetooth stop") time.sleep(2) os.system("rfkill block bluetooth") os.system("rfkill unblock bluetooth") diff --git a/etc/dbus-serialbattery/reinstall-local.sh b/etc/dbus-serialbattery/reinstall-local.sh index c9ee661d..5e9a5e18 100755 --- a/etc/dbus-serialbattery/reinstall-local.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -81,6 +81,9 @@ length=${#bms_array[@]} # always remove existing blebattery services to cleanup rm -rf /service/dbus-blebattery.* +# kill all blebattery processes +pkill -f "blebattery" + if [ $length -gt 0 ]; then echo "Found $length Bluetooth BMS in the config file!" From b423d20a3efa7cdf3d0804ab528adbde49eded78 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 5 May 2023 09:03:26 +0200 Subject: [PATCH 180/209] Fix test routine --- etc/dbus-serialbattery/lltjbd_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/lltjbd_ble.py index 9a8af779..50cc8333 100644 --- a/etc/dbus-serialbattery/lltjbd_ble.py +++ b/etc/dbus-serialbattery/lltjbd_ble.py @@ -145,7 +145,7 @@ def read_serial_data_llt(self, command): return False -async def testBLE(): +def testBLE(): import sys bat = LltJbdBle("Foo", -1, sys.argv[1]) if not bat.test_connection(): From 0301a6279d44ab1d0c1d7d828c4476a35f8f621a Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 5 May 2023 10:27:29 +0200 Subject: [PATCH 181/209] Update imports --- etc/dbus-serialbattery/{ => bms}/lltjbd_ble.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename etc/dbus-serialbattery/{ => bms}/lltjbd_ble.py (98%) diff --git a/etc/dbus-serialbattery/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py similarity index 98% rename from etc/dbus-serialbattery/lltjbd_ble.py rename to etc/dbus-serialbattery/bms/lltjbd_ble.py index 50cc8333..80c22e53 100644 --- a/etc/dbus-serialbattery/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -7,9 +7,9 @@ from bleak import BleakClient, BleakScanner, BLEDevice -from utils import * -from struct import * from lltjbd import LltJbdProtection, LltJbd +from utils import logger +from struct import unpack_from BLE_SERVICE_UUID = "0000ff00-0000-1000-8000-00805f9b34fb" BLE_CHARACTERISTICS_TX_UUID = "0000ff02-0000-1000-8000-00805f9b34fb" From 143c06e328deef7e70a21c139cfabb22761faa9c Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 5 May 2023 10:32:51 +0200 Subject: [PATCH 182/209] Revert changes --- etc/dbus-serialbattery/config.default.ini | 6 +++ etc/dbus-serialbattery/dbus-serialbattery.py | 2 +- etc/dbus-serialbattery/dbushelper.py | 56 ++++++++++---------- etc/dbus-serialbattery/utils.py | 6 +++ 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 8018e3bd..19c7f013 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -208,6 +208,12 @@ TEMP_1_NAME = Temp 1 ; Temperature sensor 2 name TEMP_2_NAME = Temp 2 +; Temperature sensor 2 name +TEMP_3_NAME = Temp 3 + +; Temperature sensor 2 name +TEMP_4_NAME = Temp 4 + ; --------- BMS specific settings --------- diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 17dfa65f..e3bc503c 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -73,7 +73,7 @@ def get_battery(_port) -> Union[Battery, None]: for test in expected_bms_types: logger.info("Testing " + test["bms"].__name__) batteryClass = test["bms"] - baud = test.get("baud") + baud = test["baud"] battery: Battery = batteryClass( port=_port, baud=baud, address=test.get("address") ) diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 980cc4d9..bce8a34b 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -48,39 +48,38 @@ def setup_instance(self): path = "/Settings/Devices/serialbattery" default_instance = "battery:1" settings = { - # Name : [PATH, VALUE, MINIMUM, MAXIMUM, SILENT] "instance": [ path + "_" + str(bms_id).replace(" ", "_") + "/ClassAndVrmInstance", default_instance, 0, 0, ], - # 'CellVoltageMin': [path + '/CellVoltageMin', MIN_CELL_VOLTAGE, 2.5, 3.65], - # 'CellVoltageMax': [path + '/CellVoltageMax', MAX_CELL_VOLTAGE, 2.5, 3.65], - # 'CellVoltageFloat': [path + '/CellVoltageFloat', FLOAT_CELL_VOLTAGE, 2.5, 3.65], - # 'VoltageMaxTime': [path + '/VoltageMaxTime', MAX_VOLTAGE_TIME_SEC, 0, 0], - # 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT, 0, 100], - # 'MaxChargeCurrent': [path + '/MaxCurrentCharge', MAX_BATTERY_CHARGE_CURRENT, 0.0, 500], - # 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', MAX_BATTERY_DISCHARGE_CURRENT, 0.0, 500], - # 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], - # 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], - # 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], - # 'SocLowWarning': [path + '/SocLowWarning', SOC_LOW_WARNING, 0, 100], - # 'SocLowAlarm': [path + '/SocLowAlarm', SOC_LOW_ALARM, 0, 100], - # 'Capacity': [path + '/Capacity', BATTERY_CAPACITY, 0, 500], - # 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', INVERT_CURRENT_MEASUREMENT, 0, 1], - # 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', CC_SOC_LIMIT1, 0, 100], - # 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', CC_SOC_LIMIT2, 0, 100], - # 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', CC_SOC_LIMIT3, 0, 100], - # 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', CC_CURRENT_LIMIT1, 0, 500], - # 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', CC_CURRENT_LIMIT2, 0, 500], - # 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', CC_CURRENT_LIMIT3, 0, 500], - # 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', DC_SOC_LIMIT1, 0, 100], - # 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', DC_SOC_LIMIT2, 0, 100], - # 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', DC_SOC_LIMIT3, 0, 100], - # 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', DC_CURRENT_LIMIT1, 0, 500], - # 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', DC_CURRENT_LIMIT2, 0, 500], - # 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', DC_CURRENT_LIMIT3, 0, 500], + # 'CellVoltageMin': [path + '/CellVoltageMin', 2.8, 0.0, 5.0], + # 'CellVoltageMax': [path + '/CellVoltageMax', 3.45, 0.0, 5.0], + # 'CellVoltageFloat': [path + '/CellVoltageFloat', 3.35, 0.0, 5.0], + # 'VoltageMaxTime': [path + '/VoltageMaxTime', 900, 0, 0], + # 'VoltageResetSocLimit': [path + '/VoltageResetSocLimit', 90, 0, 100], + # 'MaxChargeCurrent': [path + '/MaxCurrentCharge', 5, 0.0, 500], + # 'MaxDischargeCurrent': [path + '/MaxCurrentDischarge', 7, 0.0, 500], + # 'AllowDynamicChargeCurrent': [path + '/AllowDynamicChargeCurrent', 1, 0, 1], + # 'AllowDynamicDischargeCurrent': [path + '/AllowDynamicDischargeCurrent', 1, 0, 1], + # 'AllowDynamicChargeVoltage': [path + '/AllowDynamicChargeVoltage', 0, 0, 1], + # 'SocLowWarning': [path + '/SocLowWarning', 20, 0, 100], + # 'SocLowAlarm': [path + '/SocLowAlarm', 10, 0, 100], + # 'Capacity': [path + '/Capacity', '', 0, 500], + # 'EnableInvertedCurrent': [path + '/EnableInvertedCurrent', 0, 0, 1], + # 'CCMSocLimitCharge1': [path + '/CCMSocLimitCharge1', 98, 0, 100], + # 'CCMSocLimitCharge2': [path + '/CCMSocLimitCharge2', 95, 0, 100], + # 'CCMSocLimitCharge3': [path + '/CCMSocLimitCharge3', 91, 0, 100], + # 'CCMSocLimitDischarge1': [path + '/CCMSocLimitDischarge1', 10, 0, 100], + # 'CCMSocLimitDischarge2': [path + '/CCMSocLimitDischarge2', 20, 0, 100], + # 'CCMSocLimitDischarge3': [path + '/CCMSocLimitDischarge3', 30, 0, 100], + # 'CCMCurrentLimitCharge1': [path + '/CCMCurrentLimitCharge1', 5, 0, 100], + # 'CCMCurrentLimitCharge2': [path + '/CCMCurrentLimitCharge2', '', 0, 100], + # 'CCMCurrentLimitCharge3': [path + '/CCMCurrentLimitCharge3', '', 0, 100], + # 'CCMCurrentLimitDischarge1': [path + '/CCMCurrentLimitDischarge1', 5, 0, 100], + # 'CCMCurrentLimitDischarge2': [path + '/CCMCurrentLimitDischarge2', '', 0, 100], + # 'CCMCurrentLimitDischarge3': [path + '/CCMCurrentLimitDischarge3', '', 0, 100], } self.settings = SettingsDevice(get_bus(), settings, self.handle_changed_setting) @@ -223,9 +222,8 @@ def setup_vedbus(self): ) # Create battery extras - self._dbusservice.add_path("/System/MinTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MinCellTemperature", None, writeable=True) - self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True) + self._dbusservice.add_path("/System/MinTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index e5e8475f..b0239629 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -283,6 +283,12 @@ def _get_list_from_config( # Temperature sensor 2 name TEMP_2_NAME = config["DEFAULT"]["TEMP_2_NAME"] +# Temperature sensor 3 name +TEMP_3_NAME = config["DEFAULT"]["TEMP_3_NAME"] + +# Temperature sensor 2 name +TEMP_4_NAME = config["DEFAULT"]["TEMP_4_NAME"] + # --------- BMS specific settings --------- From 2f6ea71ef606c9aabdc253abb8ef16df3dd92f74 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 18 May 2023 23:55:08 +0200 Subject: [PATCH 183/209] Changes 2023.05.18 (#649) * Added: Show specific TimeToSoC points in GUI Only if 0%, 10%, 20%, 80%, 90% and/or 100% are selected * Added: Show specific TimeToSoC points in GUI Only if 0%, 10%, 20%, 80%, 90% and/or 100% are selected * fix black lint error * fix black lint error * Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 * Fix for #397 https://github.com/Louisvdw/dbus-serialbattery/pull/484 * small fixes * sort bms imports * Add support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 * Add support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 * change flake8 settings * fix black lint errors * removed wildcard imports * fixed black lint errors * change flake8 settings * remove wildcard import and fix black lint errors * removed wildcard import * fixed black lint check * removed wildcard import, fixed black lint errors * config changes * removed wildcard import, fixed black lint errors * remove old log message in handle_changed_setting() * remove old log message in handle_changed_setting() * simplified condition for Time-To-Go/Soc * simplified condition for Time-To-Go/Soc * fix renogy import * fix renogy import * added BMS info and cleanup * MNB * Revov * Sinowealth * added BMS info and cleanup * MNB * Revov * Sinowealth * moved BMS to subfolder * moved BMS to subfolder * moved BMS to subfolder * corrected installble to run correct script * Added self.unique_identifier to the battery class Used to identify a BMS when multiple BMS are connected planned for future use * Added self.unique_identifier to the battery class Used to identify a BMS when multiple BMS are connected planned for future use * changed ble service name from `dbus-blebattery-$1` to `dbus-blebattery.$1` like the non ble service * fix small errors * read installed capacity at startup * disable ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 * fix cell voltage header parser * rework daly receive routine * improve read cell voltages - only work on sufficient data, drop only the bad package on checksum error, not the complete transmission * allow read_soc to also retry serial transmission * add daly cell balance state info. cells are red only if unbalanced now * bump version * typo * moved read_serialport_data() to daly.py * revert read_serialport_data() to the state before my changes * fix connection log startup message. now voltage/current/soc are displayed correctly * black reformatting * added linear voltage recalculation interval In the config file can now be defined how often CVL, CCL and DCL is recalculated * revert Daly adaption * replaced penalty voltage calculation with automatically calculated penalty voltages to simplify config max voltage is kept until batteries are balanced * flake config change * flake config change * added linear voltage recalculation interval In the config file can now be defined how often CVL, CCL and DCL is recalculated * replaced penalty voltage calculation with automatically calculated penalty voltages to simplify config max voltage is kept until batteries are balanced * fix black lint errors * updated changelog * disabled ANT BMS by default https://github.com/Louisvdw/dbus-serialbattery/issues/479 * updated config.default.ini * fix typo * update nightly install script * Removed line * fixed error in HLPdataBMS4S * fixed wrong variable assignment `str` instead of `int` * fixed wrong variable assignment `str` instead of `int` * updated battery template * updated battery template * Fix for #450 https://github.com/Louisvdw/dbus-serialbattery/issues/450 * Read charge/discharge limit JKBMS https://github.com/Louisvdw/dbus-serialbattery/issues/4 * updated battery template * Progress with config limits reason * updated CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT default value * added SoC round for LLT/JBD * fixed log typo * updated nightly script * Fix for #450 https://github.com/Louisvdw/dbus-serialbattery/issues/450 * Read charge/discharge limit JKBMS https://github.com/Louisvdw/dbus-serialbattery/issues/4 * reworked installation procedure Bluetooth BMS is now also fetched from config.ini * updated release workflow * updated readme * Merge branch 'master' into jkbms_ble * deploy to github pages only on changes in master or docusaurus branch * cleanup * Merge branch 'master' of into jkbms_ble * GitHub pages config change * GitHub pages config change * cleanup * Renamed scripts for better reading #532 * update docusaurus dependencies * Renamed scripts for better reading #532 * update docusaurus dependencies * change sh with bash * limitation reason cleanup * limitation reason cleanup * changed default config settings FLOAT_CELL_VOLTAGE from 3.350V to 3.375V LINEAR_LIMITATION_ENABLE from False to True * changed default config settings FLOAT_CELL_VOLTAGE from 3.350V to 3.375V LINEAR_LIMITATION_ENABLE from False to True * removed testing line * Cleanup duplicated files Files were moved and not deleted * Cleanup * MOSFET temperature was displayed twice after merge * small typo fixes * updated changelog * updated changelog * Small fixes * fix disconnection behaviour: on disconnect, show '---' after 10s and 'not connected' after 60s * fix flake errors * small fix * fix disconnection behaviour & small fixes * on disconnect, show '---' after 10s and 'not connected' after 60s by @transistorgit * small fixes in shell script * added restart driver script * fixed file permission * Added: apply max voltage if CVCM_ENABLE is False before float voltage was applied * fixed type error * Added: BMS disconnect behaviour * Choose to block charge/discharge on disconnect * Trigger Venus OS alarm * Changed: Remove wildcard import from dbushelper.py * small fixes * Added: apply max voltage if CVCM_ENABLE is False before float voltage was applied * Added: BMS disconnect behaviour * Choose to block charge/discharge on disconnect * Trigger Venus OS alarm * Changed: Remove wildcard import from dbushelper.py * flake8 changes * copied lltjbd_ble from idstein:jdb_ble * Added and adapted LltJbd_Ble ATTENTION: Currently it's untested * small changes * read production date and append to hardware version * Set SOC nightly. Button is working, next is send command to bms * Added: Show additional data in device page * show self.unique_identifier as serial number * show self.production as device name * Added: JKBMS unique identifier & fixed data length * Added: JKBMS BLE unique identifier * Added: Jkbms_Ble connection_name() * Added: Daly unique identifier * Added: JKBMS BLE serial number, user defined field * Added: Show additional data in device page * show self.unique_identifier as serial number * show self.production as device name * Added: JKBMS unique identifier & fixed data length * move config.ini before update * read production date by @tranistorgit this adds the battery production date * read out daly battery code and use as unique id * moved production date and added custom field * clean battery code strip whitespace and replace one or multiple spaces with one underline if no battery code generate unique field * Daly read_capacity change Read capacity from config file, if no value provided by BMS * Daly try to fix no reply * Improvements by @transistorgit * changed value * set SOC (and date time) on button press. * fix battery code parser * format fix * format fix * fix extra long serial timeouts by calculating max time instead of loop counts * Changed: Merged all install files into one * updated install docs for nightly build * Small fixes * changed config backup * updated config file * updated changelog * debug daly * changed release workflow * changed release workflow * changed release workflow * changed release workflow * Updated from master * fix blank screen, debug daly * make Reset SoC a spin box * fix possible read_capacity problem * Daly read_balance_state() add missing return * Daly advanced troubleshooting * Changed: Improved Daily stability by a lot * fixes for disable and uninstall service was not removed and if removed, it was recreated by the serial starter * optimized USB install method * updated changelog * added missing qml to restore-gui.sh * optimized USB install method * Daly improvements * Set SoC on button press by @transistorgit * Improved driver stability by @transistorgit & @mr-manuel * moved production date and added custom field * changed jkbms_ble to dev * changed order * final daly 'broken packages handling' * Last changes for daly read problem by @transistorgit * change version in utils based on GitHub tag * test automatic release version change * Added: Configure voltage drop * test automatic release version change * fix Daly alarms * fixes small errors in bash files * fix missing driver name in restart-driver.sh * linear mode, allow max voltage on soc thesshold * Daly added one retry if failed * fixed LLT/JBD cell balancing display * rename tar after USB install fixes https://github.com/Louisvdw/dbus-serialbattery/issues/638 * add force buttons * force buttons working * fixed removing entries * implement force charge/discharge * little bit cleaner soc preset * use existing serial read/write function * add stop balancong switch * use existing serial read/write function * Changed: Get bg colors from MbStyle for dark mode * prevent short circuit evaluation * fix merge errors * added changelog info * Fix #648 --------- Co-authored-by: Bernd Stahlbock --- CHANGELOG.md | 8 +- etc/dbus-serialbattery/battery.py | 27 ++- etc/dbus-serialbattery/bms/daly.py | 175 ++++++++++++------ etc/dbus-serialbattery/bms/lifepower.py | 2 +- etc/dbus-serialbattery/bms/lltjbd.py | 38 ++++ etc/dbus-serialbattery/config.default.ini | 26 ++- etc/dbus-serialbattery/dbus-serialbattery.py | 4 +- etc/dbus-serialbattery/dbushelper.py | 22 ++- etc/dbus-serialbattery/disable.sh | 24 ++- etc/dbus-serialbattery/install.sh | 4 +- .../qml/PageBatteryCellVoltages.qml | 50 ++--- etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 18 ++ etc/dbus-serialbattery/reinstall-local.sh | 5 +- etc/dbus-serialbattery/restart-driver.sh | 2 +- etc/dbus-serialbattery/uninstall.sh | 52 ++---- etc/dbus-serialbattery/utils.py | 60 ++++-- rc/post-hook.sh | 7 + 17 files changed, 362 insertions(+), 162 deletions(-) mode change 100755 => 100644 etc/dbus-serialbattery/install.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 30822f96..ad28b18d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,10 @@ * Added: Daly BMS - Read production date and build unique identifier by @transistorgit * Added: Daly BMS - Set SoC by @transistorgit * Added: Daly BMS - Show "battery code" field that can be set in the Daly app by @transistorgit +* Added: Daly BMS - Discharge / Charge Mosfet switching over remote console/GUI https://github.com/Louisvdw/dbus-serialbattery/issues/26 by @transistorgit * Added: Device name field (found in the GUI -> SerialBattery -> Device), that show a custom string that can be set in some BMS, if available by @mr-manuel * Added: Driver uninstall script by @mr-manuel +* Added: Rename TAR file after USB/SD card install to not overwrite the data on every reboot https://github.com/Louisvdw/dbus-serialbattery/issues/638 by @mr-manuel * Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel * Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel * Added: Improved maintainability (flake8, black lint), introduced code checks and automate release build https://github.com/Louisvdw/dbus-serialbattery/pull/386 by @ppuetsch @@ -38,6 +40,7 @@ * Added: JKBMS BLE - Show serial number and "User Private Data" field that can be set in the JKBMS App to identify the BMS in a multi battery environment by @mr-manuel * Added: JKBMS BLE driver by @baranator * Added: Possibility to add `config.ini` to the root of a USB flash drive on install via the USB method by @mr-manuel +* Added: Possibility to configure a `VOLTAGE_DROP` voltage, if you are using a SmartShunt as battery monitor as there is a little voltage difference https://github.com/Louisvdw/dbus-serialbattery/discussions/632 by @mr-manuel * Added: Post install notes by @mr-manuel * Added: Read charge/discharge limits from JKBMS by @mr-manuel * Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel @@ -56,6 +59,7 @@ * Changed: `reinstall-local.sh` to recreate `/data/conf/serial-starter.d`, if deleted by `disable.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository by @mr-manuel * Changed: Added QML to `restore-gui.sh` by @mr-manuel * Changed: Bash output by @mr-manuel +* Changed: Daly BMS - Fixed BMS alerts by @mr-manuel * Changed: Daly BMS - Improved driver stability by @transistorgit & @mr-manuel * Changed: Default config file by @ppuetsch * Added missing descriptions to make it much clearer to understand by @mr-manuel @@ -73,8 +77,10 @@ * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/397 by @transistorgit * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/421 by @mr-manuel * Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/450 by @mr-manuel +* Changed: Fix for https://github.com/Louisvdw/dbus-serialbattery/issues/648 by @mr-manuel * Changed: Fixed black lint errors by @mr-manuel * Changed: Fixed cell balancing background for cells 17-24 by @mr-manuel +* Changed: Fixed cell balancing display for JBD/LLT BMS https://github.com/Louisvdw/dbus-serialbattery/issues/359 by @mr-manuel * Changed: Fixed Time-To-Go is not working, if `TIME_TO_SOC_VALUE_TYPE` is set to other than `1` https://github.com/Louisvdw/dbus-serialbattery/pull/424#issuecomment-1440511018 by @mr-manuel * Changed: Improved install workflow via USB flash drive by @mr-manuel * Changed: Improved JBD BMS soc calculation https://github.com/Louisvdw/dbus-serialbattery/pull/439 by @aaronreek @@ -83,7 +89,7 @@ * Changed: Moved Bluetooth part to `reinstall-local.sh` by @mr-manuel * Changed: Moved BMS scripts to subfolder by @mr-manuel * Changed: Removed all wildcard imports and fixed black lint errors by @mr-manuel -* Changed: Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced by @mr-manuel +* Changed: CVL calculation improvement. Removed cell voltage penalty. Replaced by automatic voltage calculation. Max voltage is kept until cells are balanced and reset when cells are inbalanced or SoC is below threshold by @mr-manuel * Changed: Renamed scripts for better reading #532 by @mr-manuel * Changed: Reworked and optimized installation scripts by @mr-manuel * Changed: Separate Time-To-Go and Time-To-SoC activation by @mr-manuel diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 108cf211..767be09d 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -207,18 +207,21 @@ def manage_charge_voltage_linear(self) -> None: voltageDiff = self.get_max_cell_voltage() - self.get_min_cell_voltage() if self.max_voltage_start_time is None: + # start timer, if max voltage is reached and cells are balanced if ( - utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum + (utils.MAX_CELL_VOLTAGE * self.cell_count) - utils.VOLTAGE_DROP + <= voltageSum and voltageDiff <= utils.CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL and self.allow_max_voltage ): self.max_voltage_start_time = time() + + # allow max voltage again, if cells are unbalanced or SoC threshold is reached elif ( - # utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc - voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT - and not self.allow_max_voltage - ): + utils.SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT > self.soc + or voltageDiff >= utils.CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT + ) and not self.allow_max_voltage: self.allow_max_voltage = True else: tDiff = time() - self.max_voltage_start_time @@ -309,9 +312,8 @@ def manage_charge_voltage_step(self) -> None: if self.max_voltage_start_time is None: # check if max voltage is reached and start timer to keep max voltage if ( - utils.MAX_CELL_VOLTAGE * self.cell_count <= voltageSum - and self.allow_max_voltage - ): + utils.MAX_CELL_VOLTAGE * self.cell_count + ) - utils.VOLTAGE_DROP <= voltageSum and self.allow_max_voltage: # example 2 self.max_voltage_start_time = time() @@ -928,3 +930,12 @@ def log_settings(self) -> None: def reset_soc_callback(self, path, value): # callback for handling reset soc request return + + def force_charging_off_callback(self, path, value): + return + + def force_discharging_off_callback(self, path, value): + return + + def turn_balancing_off_callback(self, path, value): + return diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index b358a886..54226dac 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -21,9 +21,11 @@ def __init__(self, port, baud, address): self.poll_interval = 1000 self.type = self.BATTERYTYPE self.has_settings = 1 - self.reset_soc = 0 + self.reset_soc = 100 self.soc_to_set = None self.runtime = 0 # TROUBLESHOOTING for no reply errors + self.trigger_force_disable_discharge = None + self.trigger_force_disable_charge = None # command bytes [StartFlag=A5][Address=40][Command=94][DataLength=8][8x zero bytes][checksum] command_base = b"\xA5\x40\x94\x08\x00\x00\x00\x00\x00\x00\x00\x00\x81" @@ -40,6 +42,8 @@ def __init__(self, port, baud, address): command_temp = b"\x96" command_cell_balance = b"\x97" # no reply command_alarm = b"\x98" # no reply + command_disable_discharge_mos = b"\xD9" + command_disable_charge_mos = b"\xDA" BATTERYTYPE = "Daly" LENGTH_CHECK = 1 @@ -57,9 +61,6 @@ def test_connection(self): result = self.read_status_data(ser) self.read_soc_data(ser) self.read_battery_code(ser) - self.reset_soc = ( - self.soc - ) # set to meaningful value as preset for the GUI except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") @@ -93,7 +94,7 @@ def refresh_data(self): + "s" ) - result = result and self.read_fed_data(ser) + result = self.read_fed_data(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( " |- refresh_data: read_fed_data - result: " @@ -103,7 +104,7 @@ def refresh_data(self): + "s" ) - result = result and self.read_cell_voltage_range_data(ser) + result = self.read_cell_voltage_range_data(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( " |- refresh_data: read_cell_voltage_range_data - result: " @@ -123,7 +124,7 @@ def refresh_data(self): + "s" ) - result = result and self.read_alarm_data(ser) + result = self.read_alarm_data(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( " |- refresh_data: read_alarm_data - result: " @@ -133,7 +134,7 @@ def refresh_data(self): + "s" ) - result = result and self.read_temperature_range_data(ser) + result = self.read_temperature_range_data(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( " |- refresh_data: read_temperature_range_data - result: " @@ -143,26 +144,28 @@ def refresh_data(self): + "s" ) - result = result and self.read_cells_volts(ser) + result = self.read_balance_state(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( - " |- refresh_data: read_cells_volts - result: " + " |- refresh_data: read_balance_state - result: " + str(result) + " - runtime: " + str(self.runtime) + "s" ) - result = result and self.read_balance_state(ser) + result = self.read_cells_volts(ser) and result if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( - " |- refresh_data: read_balance_state - result: " + " |- refresh_data: read_cells_volts - result: " + str(result) + " - runtime: " + str(self.runtime) + "s" ) + self.write_charge_discharge_mos(ser) + except OSError: logger.warning("Couldn't open serial port") @@ -245,57 +248,57 @@ def read_alarm_data(self, ser): if al_volt & 48: # High voltage levels - Alarm - self.voltage_high = 2 + self.protection.voltage_high = 2 elif al_volt & 15: # High voltage Warning levels - Pre-alarm - self.voltage_high = 1 + self.protection.voltage_high = 1 else: - self.voltage_high = 0 + self.protection.voltage_high = 0 if al_volt & 128: # Low voltage level - Alarm - self.voltage_low = 2 + self.protection.voltage_low = 2 elif al_volt & 64: # Low voltage Warning level - Pre-alarm - self.voltage_low = 1 + self.protection.voltage_low = 1 else: - self.voltage_low = 0 + self.protection.voltage_low = 0 if al_temp & 2: # High charge temp - Alarm - self.temp_high_charge = 2 + self.protection.temp_high_charge = 2 elif al_temp & 1: # High charge temp - Pre-alarm - self.temp_high_charge = 1 + self.protection.temp_high_charge = 1 else: - self.temp_high_charge = 0 + self.protection.temp_high_charge = 0 if al_temp & 8: # Low charge temp - Alarm - self.temp_low_charge = 2 + self.protection.temp_low_charge = 2 elif al_temp & 4: # Low charge temp - Pre-alarm - self.temp_low_charge = 1 + self.protection.temp_low_charge = 1 else: - self.temp_low_charge = 0 + self.protection.temp_low_charge = 0 if al_temp & 32: # High discharge temp - Alarm - self.temp_high_discharge = 2 + self.protection.temp_high_discharge = 2 elif al_temp & 16: # High discharge temp - Pre-alarm - self.temp_high_discharge = 1 + self.protection.temp_high_discharge = 1 else: - self.temp_high_discharge = 0 + self.protection.temp_high_discharge = 0 if al_temp & 128: # Low discharge temp - Alarm - self.temp_low_discharge = 2 + self.protection.temp_low_discharge = 2 elif al_temp & 64: # Low discharge temp - Pre-alarm - self.temp_low_discharge = 1 + self.protection.temp_low_discharge = 1 else: - self.temp_low_discharge = 0 + self.protection.temp_low_discharge = 0 # if al_crnt_soc & 2: # # High charge current - Alarm @@ -317,21 +320,21 @@ def read_alarm_data(self, ser): if al_crnt_soc & 2 or al_crnt_soc & 8: # High charge/discharge current - Alarm - self.current_over = 2 + self.protection.current_over = 2 elif al_crnt_soc & 1 or al_crnt_soc & 4: # High charge/discharge current - Pre-alarm - self.current_over = 1 + self.protection.current_over = 1 else: - self.current_over = 0 + self.protection.current_over = 0 if al_crnt_soc & 128: # Low SoC - Alarm - self.soc_low = 2 + self.protection.soc_low = 2 elif al_crnt_soc & 64: # Low SoC Warning level - Pre-alarm - self.soc_low = 1 + self.protection.soc_low = 1 else: - self.soc_low = 0 + self.protection.soc_low = 0 return True @@ -558,8 +561,16 @@ def read_serial_data_daly(self, ser, command): ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK ) if data is False: - logger.info("No reply to cmd " + bytes(command).hex()) - return False + # sleep 100 ms and retry. + sleep(0.100) + data = self.read_serialport_data( + ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK + ) + if data is False: + logger.info("No reply to cmd " + bytes(command).hex()) + return False + else: + logger.info(" |- Error cleared, received data after one retry.") if len(data) <= 12: logger.debug("Too short reply to cmd " + bytes(command).hex()) @@ -595,7 +606,7 @@ def read_serial_data_daly(self, ser, command): ) return False - # Read data from previously openned serial port + # Read data from previously opened serial port def read_serialport_data( self, ser, @@ -610,6 +621,7 @@ def read_serialport_data( # if you see a lot of errors, try to increase in steps of 0.005 sleep(0.020) + time_run = 0 time_start = time() ser.flushOutput() ser.flushInput() @@ -715,21 +727,78 @@ def write_soc_and_datetime(self, ser): logger.info(f"write soc {self.soc_to_set}%") self.soc_to_set = None # Reset value, so we will set it only once - time_start = time() - ser.flushOutput() - ser.flushInput() - ser.write(cmd) - - toread = ser.inWaiting() - while toread < 13: - sleep(0.005) - toread = ser.inWaiting() - time_run = time() - time_start - if time_run > 0.500: - logger.warning("write soc: no reply, probably failed") - return False + reply = self.read_serialport_data(ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK) - reply = ser.read(toread) if reply[4] != 1: logger.error("write soc failed") return True + + def force_charging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_charge = False + return True + + if value == 1: + self.trigger_force_disable_charge = True + return True + + return False + + def force_discharging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_discharge = False + return True + + if value == 1: + self.trigger_force_disable_discharge = True + return True + + return False + + def write_charge_discharge_mos(self, ser): + if ( + self.trigger_force_disable_charge is None + and self.trigger_force_disable_discharge is None + ): + return False + + cmd = bytearray(self.command_base) + + if self.trigger_force_disable_charge is not None: + cmd[2] = self.command_disable_charge_mos[0] + cmd[4] = 0 if self.trigger_force_disable_charge else 1 + cmd[12] = sum(cmd[:12]) & 0xFF + logger.info( + f"write force disable charging: {'true' if self.trigger_force_disable_charge else 'false'}" + ) + self.trigger_force_disable_charge = None + + reply = self.read_serialport_data( + ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK + ) + if reply is False or reply[4] != cmd[4]: + logger.error("write force disable charge/discharge failed") + return False + + if self.trigger_force_disable_discharge is not None: + cmd[2] = self.command_disable_discharge_mos[0] + cmd[4] = 0 if self.trigger_force_disable_discharge else 1 + cmd[12] = sum(cmd[:12]) & 0xFF + logger.info( + f"write force disable discharging: {'true' if self.trigger_force_disable_discharge else 'false'}" + ) + self.trigger_force_disable_discharge = None + + reply = self.read_serialport_data( + ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK + ) + if reply is False or reply[4] != cmd[4]: + logger.error("write force disable charge/discharge failed") + return False + return True diff --git a/etc/dbus-serialbattery/bms/lifepower.py b/etc/dbus-serialbattery/bms/lifepower.py index 84c93a37..5780d58c 100644 --- a/etc/dbus-serialbattery/bms/lifepower.py +++ b/etc/dbus-serialbattery/bms/lifepower.py @@ -38,7 +38,7 @@ def get_settings(self): # After successful connection get_settings will be call to set up the battery. # Set the current limits, populate cell count, etc # Return True if success, False for failure - self.max_battery_current = utils.MAX_BATTERY_CURRENT + self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT hardware_version = self.read_serial_data_eg4(self.command_hardware_version) if hardware_version: diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index a74697cc..c1bf2ae3 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -116,6 +116,43 @@ def to_protection_bits(self, byte_data): self.protection.set_short = is_bit_set(tmp[2]) def to_cell_bits(self, byte_data, byte_data_high): + # init the cell array once + if len(self.cells) == 0: + for _ in range(self.cell_count): + print("#" + str(_)) + self.cells.append(Cell(False)) + + # get up to the first 16 cells + tmp = bin(byte_data)[2:].rjust(min(self.cell_count, 16), utils.zero_char) + # 4 cells + # tmp = 0101 + # 16 cells + # tmp = 0101010101010101 + + tmp_reversed = list(reversed(tmp)) + # print(tmp_reversed) --> ['1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0'] + # [cell1, cell2, cell3, ...] + + if self.cell_count > 16: + tmp2 = bin(byte_data_high)[2:].rjust(self.cell_count - 16, utils.zero_char) + # tmp = 1100110011001100 + tmp_reversed = tmp_reversed + list(reversed(tmp2)) + # print(tmp_reversed) --> [ + # '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', + # '0', '0', '1', '1', '0', '0', '1', '1', '0', '0', '1', '1', '0', '0', '1', '1' + # ] + # [ + # cell1, cell2, ..., cell16, + # cell17, cell18, ..., cell32 + # ] + + for c in range(self.cell_count): + if is_bit_set(tmp_reversed[c]): + self.cells[c].balance = True + else: + self.cells[c].balance = False + + """ # clear the list for c in self.cells: self.cells.remove(c) @@ -128,6 +165,7 @@ def to_cell_bits(self, byte_data, byte_data_high): tmp = bin(byte_data_high)[2:].rjust(self.cell_count - 16, utils.zero_char) for bit in reversed(tmp): self.cells.append(Cell(is_bit_set(bit))) + """ def to_fet_bits(self, byte_data): tmp = bin(byte_data)[2:].rjust(2, utils.zero_char) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 8018e3bd..4b11e9b5 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -50,15 +50,18 @@ LINEAR_RECALCULATION_ON_PERC_CHANGE = 5 ; SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it switches back to max voltage. ; Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to ; CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to float voltage after 300 (fixed) -; additional seconds. After cell voltage difference is greater or equal to -; CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT it switches back to max voltage. +; additional seconds. +; After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT +; OR +; SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT +; it switches back to max voltage. ; Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to ; float voltage of 53.6V to don't stress the batteries. Allow max voltage of 55.2V again, if SoC is ; once below 90% ; OR ; The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float ; voltage of 53.6V after 300 additional seconds to don't stress the batteries. Allow max voltage of -; 55.2V again if max cell difference is above 0.050V +; 55.2V again if max cell difference is above 0.080V or SoC below 90%. ; Charge voltage control management enable (True/False). CVCM_ENABLE = True @@ -218,7 +221,7 @@ SOC_LOW_WARNING = 20 SOC_LOW_ALARM = 10 ; -- Daly settings -; Battery capacity (amps) if the BMS does not support reading it +; Battery capacity (amps), if the BMS does not support reading it BATTERY_CAPACITY = 50 ; Invert Battery Current. Default non-inverted. Set to -1 to invert INVERT_CURRENT_MEASUREMENT = 1 @@ -228,3 +231,18 @@ GREENMETER_ADDRESS = 1 LIPRO_START_ADDRESS = 2 LIPRO_END_ADDRESS = 4 LIPRO_CELL_COUNT = 15 + + +; --------- Battery monitor specific settings --------- +; If you are using a SmartShunt or something else as a battery monitor, the battery voltage reported +; from the BMS and SmartShunt could differ. This causes, that the driver never goapplies the float voltage, +; since max voltage is never reached. +; Example: +; cell count: 16 +; MAX_CELL_VOLTAGE = 3.45 +; max voltage calculated = 16 * 3.45 = 55.20 +; CVL is set to 55.20 and the battery is now charged until the SmartShunt measures 55.20 V. The BMS +; now measures 55.05 V since there is a voltage drop of 0.15 V. Since the dbus-serialbattery measures +; 55.05 V the max voltage is never reached for the driver and max voltage is kept forever. +; Set VOLTAGE_DROP to 0.15 +VOLTAGE_DROP = 0.00 diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index 68265a07..b50c938c 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -97,9 +97,7 @@ def get_port() -> str: logger.info("No Port needed") return "/dev/tty/USB9" - logger.info( - "dbus-serialbattery v" + str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION - ) + logger.info("dbus-serialbattery v" + str(utils.DRIVER_VERSION)) port = get_port() battery = None diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 555cb1ba..ccf7181e 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -121,9 +121,7 @@ def setup_vedbus(self): self._dbusservice.add_path( "/ProductName", "SerialBattery(" + self.battery.type + ")" ) - self._dbusservice.add_path( - "/FirmwareVersion", str(utils.DRIVER_VERSION) + utils.DRIVER_SUBVERSION - ) + self._dbusservice.add_path("/FirmwareVersion", str(utils.DRIVER_VERSION)) self._dbusservice.add_path("/HardwareVersion", self.battery.hardware_version) self._dbusservice.add_path("/Connected", 1) self._dbusservice.add_path( @@ -253,6 +251,24 @@ def setup_vedbus(self): self._dbusservice.add_path("/Io/AllowToCharge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToDischarge", 0, writeable=True) self._dbusservice.add_path("/Io/AllowToBalance", 0, writeable=True) + self._dbusservice.add_path( + "/Io/ForceChargingOff", + 0, + writeable=True, + onchangecallback=self.battery.force_charging_off_callback, + ) + self._dbusservice.add_path( + "/Io/ForceDischargingOff", + 0, + writeable=True, + onchangecallback=self.battery.force_discharging_off_callback, + ) + self._dbusservice.add_path( + "/Io/TurnBalancingOff", + 0, + writeable=True, + onchangecallback=self.battery.turn_balancing_off_callback, + ) # self._dbusservice.add_path('/SystemSwitch', 1, writeable=True) # Create the alarms diff --git a/etc/dbus-serialbattery/disable.sh b/etc/dbus-serialbattery/disable.sh index 34304cd5..f1902881 100755 --- a/etc/dbus-serialbattery/disable.sh +++ b/etc/dbus-serialbattery/disable.sh @@ -4,16 +4,27 @@ #set -x # handle read only mounts -sh /opt/victronenergy/swupdate-scripts/remount-rw.sh +bash /opt/victronenergy/swupdate-scripts/remount-rw.sh -# remove files, don't use variables here, since on an error the whole /opt/victronenergy gets deleted +# remove driver from serial starter rm -f /data/conf/serial-starter.d/dbus-serialbattery.conf +# kill serial starter, to reload changes +pkill -f "/opt/victronenergy/serial-starter/serial-starter.sh" + +# remove services rm -rf /service/dbus-serialbattery.* rm -rf /service/dbus-blebattery.* +# kill driver, if running +pkill -f "python .*/dbus-serialbattery.py" +pkill -f "blebattery" + # remove install script from rc.local sed -i "/bash \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local +# remove cronjob +sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root + ### needed for upgrading from older versions | start ### # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 @@ -25,10 +36,5 @@ sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.loca sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local ### needed for upgrading from older versions | end ### - -# kill serial starter, to reload changes -pkill -f "/opt/victronenergy/serial-starter/serial-starter.sh" - -# kill driver, if running -pkill -f "serialbattery" -pkill -f "blebattery" +echo "The dbus-serialbattery driver was disabled". +echo diff --git a/etc/dbus-serialbattery/install.sh b/etc/dbus-serialbattery/install.sh old mode 100755 new mode 100644 index 17a12ed3..c54c92c6 --- a/etc/dbus-serialbattery/install.sh +++ b/etc/dbus-serialbattery/install.sh @@ -49,8 +49,8 @@ fi ## specific version if [ "$version" = "specific version" ]; then # read the url - read -p "Enter the url of the \"venus-data.tar.gz\" you want to install: " tar_url - wget -O /tmp/venus-data.tar.gz $tar_url + read -r -p "Enter the url of the \"venus-data.tar.gz\" you want to install: " tar_url + wget -O /tmp/venus-data.tar.gz "$tar_url" if [ $? -ne 0 ]; then echo "Error during downloading the TAR file. Please check, if the URL is correct." exit diff --git a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml index 112564df..ea723a89 100644 --- a/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml +++ b/etc/dbus-serialbattery/qml/PageBatteryCellVoltages.qml @@ -4,6 +4,8 @@ import com.victron.velib 1.0 MbPage { id: root property string bindPrefix + property MbStyle style: MbStyle{} + property VBusItem _b1: VBusItem { bind: service.path("/Balances/Cell1") } property VBusItem _b2: VBusItem { bind: service.path("/Balances/Cell2") } property VBusItem _b3: VBusItem { bind: service.path("/Balances/Cell3") } @@ -52,30 +54,30 @@ MbPage { property VBusItem volt22: VBusItem { bind: service.path("/Voltages/Cell22") } property VBusItem volt23: VBusItem { bind: service.path("/Voltages/Cell23") } property VBusItem volt24: VBusItem { bind: service.path("/Voltages/Cell24") } - property string c1: _b1.valid && _b1.text == "1" ? "#ff0000" : "#ddd" - property string c2: _b2.valid && _b2.text == "1" ? "#ff0000" : "#ddd" - property string c3: _b3.valid && _b3.text == "1" ? "#ff0000" : "#ddd" - property string c4: _b4.valid && _b4.text == "1" ? "#ff0000" : "#ddd" - property string c5: _b5.valid && _b5.text == "1" ? "#ff0000" : "#ddd" - property string c6: _b6.valid && _b6.text == "1" ? "#ff0000" : "#ddd" - property string c7: _b7.valid && _b7.text == "1" ? "#ff0000" : "#ddd" - property string c8: _b8.valid && _b8.text == "1" ? "#ff0000" : "#ddd" - property string c9: _b9.valid && _b9.text == "1" ? "#ff0000" : "#ddd" - property string c10: _b10.valid && _b10.text == "1" ? "#ff0000" : "#ddd" - property string c11: _b11.valid && _b11.text == "1" ? "#ff0000" : "#ddd" - property string c12: _b12.valid && _b12.text == "1" ? "#ff0000" : "#ddd" - property string c13: _b13.valid && _b13.text == "1" ? "#ff0000" : "#ddd" - property string c14: _b14.valid && _b14.text == "1" ? "#ff0000" : "#ddd" - property string c15: _b15.valid && _b15.text == "1" ? "#ff0000" : "#ddd" - property string c16: _b16.valid && _b16.text == "1" ? "#ff0000" : "#ddd" - property string c17: _b17.valid && _b17.text == "1" ? "#ff0000" : "#ddd" - property string c18: _b18.valid && _b18.text == "1" ? "#ff0000" : "#ddd" - property string c19: _b19.valid && _b19.text == "1" ? "#ff0000" : "#ddd" - property string c20: _b20.valid && _b20.text == "1" ? "#ff0000" : "#ddd" - property string c21: _b21.valid && _b21.text == "1" ? "#ff0000" : "#ddd" - property string c22: _b22.valid && _b22.text == "1" ? "#ff0000" : "#ddd" - property string c23: _b23.valid && _b23.text == "1" ? "#ff0000" : "#ddd" - property string c24: _b24.valid && _b24.text == "1" ? "#ff0000" : "#ddd" + property string c1: _b1.valid && _b1.text == "1" ? "#ff0000" : style.borderColor + property string c2: _b2.valid && _b2.text == "1" ? "#ff0000" : style.borderColor + property string c3: _b3.valid && _b3.text == "1" ? "#ff0000" : style.borderColor + property string c4: _b4.valid && _b4.text == "1" ? "#ff0000" : style.borderColor + property string c5: _b5.valid && _b5.text == "1" ? "#ff0000" : style.borderColor + property string c6: _b6.valid && _b6.text == "1" ? "#ff0000" : style.borderColor + property string c7: _b7.valid && _b7.text == "1" ? "#ff0000" : style.borderColor + property string c8: _b8.valid && _b8.text == "1" ? "#ff0000" : style.borderColor + property string c9: _b9.valid && _b9.text == "1" ? "#ff0000" : style.borderColor + property string c10: _b10.valid && _b10.text == "1" ? "#ff0000" : style.borderColor + property string c11: _b11.valid && _b11.text == "1" ? "#ff0000" : style.borderColor + property string c12: _b12.valid && _b12.text == "1" ? "#ff0000" : style.borderColor + property string c13: _b13.valid && _b13.text == "1" ? "#ff0000" : style.borderColor + property string c14: _b14.valid && _b14.text == "1" ? "#ff0000" : style.borderColor + property string c15: _b15.valid && _b15.text == "1" ? "#ff0000" : style.borderColor + property string c16: _b16.valid && _b16.text == "1" ? "#ff0000" : style.borderColor + property string c17: _b17.valid && _b17.text == "1" ? "#ff0000" : style.borderColor + property string c18: _b18.valid && _b18.text == "1" ? "#ff0000" : style.borderColor + property string c19: _b19.valid && _b19.text == "1" ? "#ff0000" : style.borderColor + property string c20: _b20.valid && _b20.text == "1" ? "#ff0000" : style.borderColor + property string c21: _b21.valid && _b21.text == "1" ? "#ff0000" : style.borderColor + property string c22: _b22.valid && _b22.text == "1" ? "#ff0000" : style.borderColor + property string c23: _b23.valid && _b23.text == "1" ? "#ff0000" : style.borderColor + property string c24: _b24.valid && _b24.text == "1" ? "#ff0000" : style.borderColor title: service.description + " | Cell Voltages" model: VisibleItemModel { diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index e6ad7106..21bfedf4 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -49,6 +49,24 @@ MbPage { ] } + MbSwitch { + name: qsTr("Force charging off") + bind: Utils.path(bindPrefix, "/Io/ForceChargingOff") + show: item.valid + } + + MbSwitch { + name: qsTr("Force discharging off") + bind: Utils.path(bindPrefix, "/Io/ForceDischargingOff") + show: item.valid + } + + MbSwitch { + name: qsTr("Turn balancing off") + bind: Utils.path(bindPrefix, "/Io/TurnBalancingOff") + show: item.valid + } + MbItemOptions { description: qsTr("External relay") bind: Utils.path(bindPrefix, "/Io/ExternalRelay") diff --git a/etc/dbus-serialbattery/reinstall-local.sh b/etc/dbus-serialbattery/reinstall-local.sh index f822e026..b082c620 100755 --- a/etc/dbus-serialbattery/reinstall-local.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -141,9 +141,10 @@ fi # remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 rm -rf /service/dbus-blebattery-* # remove old install script from rc.local -sed -i "/sh \/data\/etc\/$DRIVERNAME\/reinstalllocal.sh/d" /data/rc.local +sed -i "/^sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local +sed -i "/^sh \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local # remove old entry from rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local +sed -i "/^sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local ### needed for upgrading from older versions | end ### diff --git a/etc/dbus-serialbattery/restart-driver.sh b/etc/dbus-serialbattery/restart-driver.sh index 853a8b97..88a15800 100755 --- a/etc/dbus-serialbattery/restart-driver.sh +++ b/etc/dbus-serialbattery/restart-driver.sh @@ -10,7 +10,7 @@ cp -f /data/etc/dbus-serialbattery/config.ini /opt/victronenergy/dbus-serialbatt # svc -d -u /service/dbus-serialbattery # kill driver, if running. It gets restarted by the service daemon -pkill -f "python .*/$DRIVERNAME.py" +pkill -f "python .*/dbus-serialbattery.py" # get BMS list from config file diff --git a/etc/dbus-serialbattery/uninstall.sh b/etc/dbus-serialbattery/uninstall.sh index d11df02f..94100a9d 100755 --- a/etc/dbus-serialbattery/uninstall.sh +++ b/etc/dbus-serialbattery/uninstall.sh @@ -3,41 +3,15 @@ # remove comment for easier troubleshooting #set -x -# handle read only mounts -bash /opt/victronenergy/swupdate-scripts/remount-rw.sh +# disable driver +bash /data/etc/dbus-serialbattery/disable.sh -# remove files, don't use variables here, since on an error the whole /opt/victronenergy gets deleted -rm -f /data/conf/serial-starter.d/dbus-serialbattery.conf + +# remove files in Victron directory. Don't use variables here, +# since on an error the whole /opt/victronenergy gets deleted rm -rf /opt/victronenergy/service/dbus-serialbattery rm -rf /opt/victronenergy/service-templates/dbus-serialbattery rm -rf /opt/victronenergy/dbus-serialbattery -rm -rf /service/dbus-serialbattery.* -rm -rf /service/dbus-blebattery.* - -# remove install-script from rc.local -sed -i "/bash \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local - -# remove cronjob -sed -i "/5 0,12 \* \* \* \/etc\/init.d\/bluetooth restart/d" /var/spool/cron/root - - -### needed for upgrading from older versions | start ### -# remove old drivers before changing from dbus-blebattery-$1 to dbus-blebattery.$1 -rm -rf /service/dbus-blebattery-* -# remove old install script from rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstalllocal.sh/d" /data/rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/reinstall-local.sh/d" /data/rc.local -# remove old entry from rc.local -sed -i "/sh \/data\/etc\/dbus-serialbattery\/installble.sh/d" /data/rc.local -### needed for upgrading from older versions | end ### - - -# kill serial starter, to reload changes -pkill -f "/opt/victronenergy/serial-starter/serial-starter.sh" - -# kill driver, if running -pkill -f "serialbattery" -pkill -f "blebattery" # restore GUI changes @@ -45,10 +19,10 @@ pkill -f "blebattery" # uninstall modules -read -r -p "Do you also want to uninstall bleak, python3-pip and python3-modules? If you don't know select y. [Y/n] " response +read -r -p "Do you want to uninstall bleak, python3-pip and python3-modules? If you don't know just press enter. [y/N] " response echo response=${response,,} # tolower -if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then +if [[ $response =~ ^(y) ]]; then echo "Uninstalling modules..." pip3 uninstall bleak opkg remove python3-pip python3-modules @@ -57,5 +31,15 @@ if [[ $response =~ ^(y| ) ]] || [[ -z $response ]]; then fi -echo "The driver was uninstalled. To delete also the install files run \"rm -rf /data/etc/dbus-serialbattery\" now." +read -r -p "Do you want to delete the install and configuration files in \"/data/etc/dbus-serialbattery\"? If you don't know just press enter. [y/N] " response +echo +response=${response,,} # tolower +if [[ $response =~ ^(y) ]]; then + rm -rf /data/etc/dbus-serialbattery + echo "The folder \"/data/etc/dbus-serialbattery\" was removed." + echo +fi + + +echo "The dbus-serialbattery driver was uninstalled. Please reboot." echo diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 74aa7d22..e36782da 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -35,8 +35,7 @@ def _get_list_from_config( # if not specified: baud = 9600 # Constants - Need to dynamically get them in future -DRIVER_VERSION = "1.0" -DRIVER_SUBVERSION = ".0 (20230508)" +DRIVER_VERSION = "1.0.20230518dev" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" @@ -64,9 +63,10 @@ def _get_list_from_config( # --------- Charge mode --------- # Choose the mode for voltage / current limitations (True / False) -# False is a step mode. This is the default with limitations on hard boundary steps -# True is a linear mode. For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) -# For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded +# False is a step mode: This is the default with limitations on hard boundary steps +# True is a linear mode: +# For CCL and DCL the values between the steps are calculated for smoother values (by WaldemarFech) +# For CVL max battery voltage is calculated dynamically in order that the max cell voltage is not exceeded LINEAR_LIMITATION_ENABLE = "True" == config["DEFAULT"]["LINEAR_LIMITATION_ENABLE"] # Specify in seconds how often the penalty should be recalculated @@ -79,17 +79,26 @@ def _get_list_from_config( # --------- Charge Voltage limitation (affecting CVL) --------- -# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float voltage (FLOAT_CELL_VOLTAGE * cell count) and back -# Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it -# switches back to max voltage. -# Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to -# float voltage after 300 (fixed) additional seconds. After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT -# it switches back to max voltage. -# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to float voltage of 53.6V to don't stress the batteries. -# Allow max voltage of 55.2V again, if SoC is once below 90% +# Description: Limit max charging voltage (MAX_CELL_VOLTAGE * cell count), switch from max voltage to float +# voltage (FLOAT_CELL_VOLTAGE * cell count) and back +# False: Max charging voltage is always kept +# True: Max charging voltage is reduced based on charge mode +# Step mode: After max voltage is reached for MAX_VOLTAGE_TIME_SEC it switches to float voltage. After +# SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT it switches back to max voltage. +# Linear mode: After max voltage is reachend and cell voltage difference is smaller or equal to +# CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL it switches to float voltage after 300 (fixed) +# additional seconds. +# After cell voltage difference is greater or equal to CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT +# OR +# SoC is below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT +# it switches back to max voltage. +# Example: The battery reached max voltage of 55.2V and hold it for 900 seconds, the the CVL is switched to +# float voltage of 53.6V to don't stress the batteries. Allow max voltage of 55.2V again, if SoC is +# once below 90% # OR -# The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float voltage of 53.6V after 300 additional seconds -# to don't stress the batteries. Allow max voltage of 55.2V again if max cell difference is above 0.050V +# The battery reached max voltage of 55.2V and the max cell difference is 0.010V, then switch to float +# voltage of 53.6V after 300 additional seconds to don't stress the batteries. Allow max voltage of +# 55.2V again if max cell difference is above 0.080V or SoC below 90%. # Charge voltage control management enable (True/False). CVCM_ENABLE = "True" == config["DEFAULT"]["CVCM_ENABLE"] @@ -99,14 +108,16 @@ def _get_list_from_config( config["DEFAULT"]["CELL_VOLTAGE_DIFF_KEEP_MAX_VOLTAGE_UNTIL"] ) # Specify cell voltage diff where CVL limit is reset to max voltage, if value get above +# the cells are considered as imbalanced, if the cell diff exceeds 5% of the nominal cell voltage +# e.g. 3.2 V * 5 / 100 = 0.160 V CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT = float( config["DEFAULT"]["CELL_VOLTAGE_DIFF_TO_RESET_VOLTAGE_LIMIT"] ) # -- CVL Reset based on SoC option -# Reset max voltage after +# Specify how long the max voltage should be kept, if reached then switch to float voltage MAX_VOLTAGE_TIME_SEC = float(config["DEFAULT"]["MAX_VOLTAGE_TIME_SEC"]) -# Specify SoC where CVL limit is reset to max voltage +# Specify SoC where CVL limit is reset to max voltage, if value gets below SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT = float( config["DEFAULT"]["SOC_LEVEL_TO_RESET_VOLTAGE_LIMIT"] ) @@ -305,6 +316,21 @@ def _get_list_from_config( LIPRO_CELL_COUNT = int(config["DEFAULT"]["LIPRO_CELL_COUNT"]) +# --------- Battery monitor specific settings --------- +# If you are using a SmartShunt or something else as a battery monitor, the battery voltage reported +# from the BMS and SmartShunt could differ. This causes, that the driver never goapplies the float voltage, +# since max voltage is never reached. +# Example: +# cell count: 16 +# MAX_CELL_VOLTAGE = 3.45 +# max voltage calculated = 16 * 3.45 = 55.20 +# CVL is set to 55.20 and the battery is now charged until the SmartShunt measures 55.20 V. The BMS +# now measures 55.05 V since there is a voltage drop of 0.15 V. Since the dbus-serialbattery measures +# 55.05 V the max voltage is never reached for the driver and max voltage is kept forever. +# Set VOLTAGE_DROP to 0.15 +VOLTAGE_DROP = float(config["DEFAULT"]["VOLTAGE_DROP"]) + + # --------- Functions --------- def constrain(val, min_val, max_val): if min_val > max_val: diff --git a/rc/post-hook.sh b/rc/post-hook.sh index 37cb7b2b..b4997054 100644 --- a/rc/post-hook.sh +++ b/rc/post-hook.sh @@ -23,3 +23,10 @@ fi # run reinstall local bash /data/etc/dbus-serialbattery/reinstall-local.sh + +# rename the venus-data.tar.gz else the data is overwritten, if the USB is not removed +for dir in /media/*; do + if [ -f "/media/$dir/venus-data.tar.gz" ]; then + mv "/media/$dir/venus-data.tar.gz" "/media/$dir/venus-data_installed.tar.gz" + fi +done From 5242573d5bdafe5b6fa23dd55d5dbf06cf009f70 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 09:54:07 +0200 Subject: [PATCH 184/209] Remove unused --- etc/dbus-serialbattery/install-local.sh | 23 -------- etc/dbus-serialbattery/install-nightly.sh | 65 ----------------------- 2 files changed, 88 deletions(-) delete mode 100755 etc/dbus-serialbattery/install-local.sh delete mode 100755 etc/dbus-serialbattery/install-nightly.sh diff --git a/etc/dbus-serialbattery/install-local.sh b/etc/dbus-serialbattery/install-local.sh deleted file mode 100755 index 61ffccf1..00000000 --- a/etc/dbus-serialbattery/install-local.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -# remove comment for easier troubleshooting -#set -x - -# backup config.ini -if [ -f "/data/etc/dbus-serialbattery/config.ini" ]; then - mv /data/etc/dbus-serialbattery/config.ini /data/etc/config.ini -fi - -# remove old driver -rm -rf /data/etc/dbus-serialbattery - -# extract driver -tar -zxf ./venus-data.tar.gz -C /data - -# restore config.ini -if [ -f "/data/etc/config.ini" ]; then - mv /data/etc/config.ini /data/etc/dbus-serialbattery/config.ini -fi - -# install driver -sh /data/etc/dbus-serialbattery/reinstall-local.sh diff --git a/etc/dbus-serialbattery/install-nightly.sh b/etc/dbus-serialbattery/install-nightly.sh deleted file mode 100755 index 061699b8..00000000 --- a/etc/dbus-serialbattery/install-nightly.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# remove comment for easier troubleshooting -#set -x - -PS3="Select the branch from wich you want to install the current code (possible bugs included): " - -select branch in master jkbms_ble quit -do - case $branch in - master) - echo "Selected branch: $branch" - #echo "Selected number: $REPLY" - break - ;; - jkbms_ble) - echo "Selected branch: $branch" - #echo "Selected number: $REPLY" - break - ;; - quit) - exit 0 - ;; - *) - echo "Invalid option $REPLY" - ;; - esac -done - - -cd /tmp - -# clean already extracted folder -rm -rf /tmp/dbus-serialbattery-$branch - -# download driver -wget -O $branch.zip https://github.com/Louisvdw/dbus-serialbattery/archive/refs/heads/$branch.zip - -# extract archive -unzip -q $branch.zip - -# backup config.ini -if [ -f "/data/etc/dbus-serialbattery/config.ini" ]; then - mv /data/etc/dbus-serialbattery/config.ini /data/etc/config.ini -fi - -# remove old driver -rm -rf /data/etc/dbus-serialbattery - -# copy driver -cp -rf /tmp/dbus-serialbattery-$branch/etc/dbus-serialbattery/ /data/etc - -# restore config.ini -if [ -f "/data/etc/config.ini" ]; then - mv /data/etc/config.ini /data/etc/dbus-serialbattery/config.ini -fi - -# set permissions -chmod +x /data/etc/dbus-serialbattery/*.sh -chmod +x /data/etc/dbus-serialbattery/*.py -chmod +x /data/etc/dbus-serialbattery/service/run -chmod +x /data/etc/dbus-serialbattery/service/log/run - -# run install script -bash /data/etc/dbus-serialbattery/reinstall-local.sh From da6ef9e1dd50854ec8ca1e90d5fa01e73af481a2 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 14:26:15 +0200 Subject: [PATCH 185/209] Add reading current limits --- etc/dbus-serialbattery/bms/lltjbd.py | 233 +++++++++++++++++++++-- etc/dbus-serialbattery/bms/lltjbd_ble.py | 24 +-- 2 files changed, 225 insertions(+), 32 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index d9930508..3f33cd97 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -5,6 +5,166 @@ from struct import unpack_from import struct +# Protocol registers +REG_ENTER_FACTORY = 0x00 +REG_EXIT_FACTORY = 0x01 +# REG_UNKNOWN = 0x02 +REG_GENERAL = 0x03 +REG_CELL = 0x04 +REG_HARDWARE = 0x05 +# Firmware 0x16+ +REG_USE_PASSWORD = 0x06 +REG_SET_PASSWORD = 0x07 +# REG_UNKNOWN2 = 0x08 - Maybe define master password? +REG_CLEAR_PASSWORD = 0x09 + +REG_FRESET = 0x0A + +REG_DESIGN_CAP = 0x10 +REG_CYCLE_CAP = 0x11 +REG_CAP_100 = 0x12 +REG_CAP_0 = 0x13 +REG_SELF_DSG_RATE = 0x14 +REG_MFG_DATE = 0x15 +REG_SERIAL_NUM = 0x16 +REG_CYCLE_CNT = 0x17 +REG_CHGOT = 0x18 +REG_CHGOT_REL = 0x19 +REG_CHGUT = 0x1A +REG_CHGUT_REL = 0x1B +REG_DSGOT = 0x1C +REG_DSGOT_REL = 0x1D +REG_DSGUT = 0x1E +REG_DSGUT_REL = 0x1F +REG_POVP = 0x20 +REG_POVP_REL = 0x21 +REG_PUVP = 0x22 +REG_PUVP_REL = 0x23 +REG_COVP = 0x24 +REG_COVP_REL = 0x25 +REG_CUVP = 0x26 +REG_CUVP_REL = 0x27 +REG_CHGOC = 0x28 +REG_DSGOC = 0x29 +REG_BAL_START = 0x2A +REG_BAL_WINDOW = 0x2B +REG_SHUNT_RES = 0x2C +REG_FUNC_CONFIG = 0x2D +REG_NTC_CONFIG = 0x2E +REG_CELL_CNT = 0x2F +REG_FET_TIME = 0x30 +REG_LED_TIME = 0x31 +REG_CAP_80 = 0x32 +REG_CAP_60 = 0x33 +REG_CAP_40 = 0x34 +REG_CAP_20 = 0x35 +REG_COVP_HIGH = 0x36 +REG_CUVP_HIGH = 0x37 +REG_SC_DSGOC2 = 0x38 +REG_CXVP_HIGH_DELAY_SC_REL = 0x39 +REG_CHG_T_DELAYS = 0x3A +REG_DSG_T_DELAYS = 0x3B +REG_PACK_V_DELAYS = 0x3C +REG_CELL_V_DELAYS = 0x3D +REG_CHGOC_DELAYS = 0x3E +REG_DSGOC_DELAYS = 0x3F +REG_GPSOFF = 0x40 +REG_GPSOFF_TIME = 0x41 +REG_CAP_90 = 0x42 +REG_CAP_70 = 0x43 +REG_CAP_50 = 0x44 +REG_CAP_30 = 0x45 +REG_CAP_10 = 0x46 +REG_CAP2_100 = 0x47 + +# [0x48, 0x9F] - 87 registers + +REG_MFGNAME = 0xA0 +REG_MODEL = 0xA1 +REG_BARCODE = 0xA2 +REG_ERROR = 0xAA +# 0xAB +# 0xAC +REG_CAL_CUR_IDLE = 0xAD +REG_CAL_CUR_CHG = 0xAE +REG_CAL_CUR_DSG = 0xAF + +REG_CAL_V_CELL_01 = 0xB0 +REG_CAL_V_CELL_02 = 0xB1 +REG_CAL_V_CELL_03 = 0xB2 +REG_CAL_V_CELL_04 = 0xB3 +REG_CAL_V_CELL_05 = 0xB4 +REG_CAL_V_CELL_06 = 0xB5 +REG_CAL_V_CELL_07 = 0xB6 +REG_CAL_V_CELL_08 = 0xB7 +REG_CAL_V_CELL_09 = 0xB8 +REG_CAL_V_CELL_10 = 0xB9 +REG_CAL_V_CELL_11 = 0xBA +REG_CAL_V_CELL_12 = 0xBB +REG_CAL_V_CELL_13 = 0xBC +REG_CAL_V_CELL_14 = 0xBD +REG_CAL_V_CELL_15 = 0xBE +REG_CAL_V_CELL_16 = 0xBF +REG_CAL_V_CELL_17 = 0xC0 +REG_CAL_V_CELL_18 = 0xC1 +REG_CAL_V_CELL_19 = 0xC2 +REG_CAL_V_CELL_20 = 0xC3 +REG_CAL_V_CELL_21 = 0xC4 +REG_CAL_V_CELL_22 = 0xC5 +REG_CAL_V_CELL_23 = 0xC6 +REG_CAL_V_CELL_24 = 0xC7 +REG_CAL_V_CELL_25 = 0xC8 +REG_CAL_V_CELL_26 = 0xC9 +REG_CAL_V_CELL_27 = 0xCA +REG_CAL_V_CELL_28 = 0xCB +REG_CAL_V_CELL_29 = 0xCC +REG_CAL_V_CELL_30 = 0xCD +REG_CAL_V_CELL_31 = 0xCE +REG_CAL_V_CELL_32 = 0xCF + +REG_CAL_T_NTC_0 = 0xD0 +REG_CAL_T_NTC_1 = 0xD1 +REG_CAL_T_NTC_2 = 0xD2 +REG_CAL_T_NTC_3 = 0xD3 +REG_CAL_T_NTC_4 = 0xD4 +REG_CAL_T_NTC_5 = 0xD5 +REG_CAL_T_NTC_6 = 0xD6 +REG_CAL_T_NTC_7 = 0xD7 + +REG_CAP_REMAINING = 0xE0 +REG_CTRL_MOSFET = 0xE1 +REG_CTRL_BALANCE = 0xE2 +REG_RESET = 0xE3 + +# Protocol commands +CMD_ENTER_FACTORY_MODE = b'\x56\x78' +CMD_EXIT_FACTORY_MODE = b'\x00\x00' +CMD_EXIT_AND_SAVE_FACTORY_MODE = b'\x28\x28' + + +def checksum(payload): + return (0x10000 - sum(payload)) % 0x10000 + + +def cmd(op, reg, data): + payload = [reg, len(data)] + list(data) + chksum = checksum(payload) + data = [0xDD, op] + payload + [chksum, 0x77] + format = f'>BB{len(payload)}BHB' + return struct.pack(format, *data) + + +def readCmd(reg, data=None): + if data is None: + data = [] + return cmd(0xA5, reg, data) + + +def writeCmd(reg, data=None): + if data is None: + data = [] + return cmd(0x5A, reg, data) + class LltJbdProtection(Protection): def __init__(self): @@ -52,15 +212,18 @@ def __init__(self, port, baud, address): self.protection = LltJbdProtection() self.type = self.BATTERYTYPE self._product_name: str = "" + self.factory_mode = False + self.writable = False # degree_sign = u'\N{DEGREE SIGN}' - command_general = b"\xDD\xA5\x03\x00\xFF\xFD\x77" - command_cell = b"\xDD\xA5\x04\x00\xFF\xFC\x77" - command_hardware = b"\xDD\xA5\x05\x00\xFF\xFB\x77" BATTERYTYPE = "LLT/JBD" LENGTH_CHECK = 6 LENGTH_POS = 3 + command_general = readCmd(REG_GENERAL) # b"\xDD\xA5\x03\x00\xFF\xFD\x77" + command_cell = readCmd(REG_CELL) # b"\xDD\xA5\x04\x00\xFF\xFC\x77" + command_hardware = readCmd(REG_HARDWARE) # b"\xDD\xA5\x05\x00\xFF\xFB\x77" + def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. # The result or call should be unique to this BMS. Battery name or version, etc. @@ -84,6 +247,14 @@ def get_settings(self): self.read_gen_data() self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + with self.eeprom(writable=False): + charge_over_current = self.read_serial_data_llt(readCmd(REG_CHGOC)) + if charge_over_current: + self.max_battery_charge_current = float(unpack_from(">h", charge_over_current)[0] / 100.0) + discharge_over_current = self.read_serial_data_llt(readCmd(REG_DSGOC)) + if discharge_over_current: + self.max_battery_discharge_current = float(unpack_from(">h", discharge_over_current)[0] / -100.0) + return True def refresh_data(self): @@ -244,18 +415,56 @@ def read_hardware_data(self): logger.debug(self._product_name) return True + @staticmethod + def validate_packet(data): + if not data: + return False + + if data is False: + return False + + start, op, status, payload_length = unpack_from("BBBB", data) + if start != 0xDD: + logger.error(">>> ERROR: Invalid response packet. Expected begin packet character 0xDD") + if status != 0x0: + logger.warn(">>> WARN: BMS rejected request. Status " + status) + return False + if len(data) != payload_length + 7: + logger.error(">>> ERROR: BMS send insufficient data. Received " + str(len(data)) + " expected " + str( + payload_length + 7)) + return False + chk_sum, end = unpack_from(">HB", data, payload_length + 4) + if end != 0x77: + logger.error(">>> ERROR: Incorrect Reply. Expected end packet character 0x77") + return False + if chk_sum != checksum(data[2:-3]): + logger.error(">>> ERROR: Invalid checksum.") + return False + + payload = data[4: payload_length + 4] + + return payload + def read_serial_data_llt(self, command): data = read_serial_data( command, self.port, self.baud_rate, self.LENGTH_POS, self.LENGTH_CHECK ) - if data is False: - return False + return self.validate_packet(data) - start, flag, command_ret, length = unpack_from("BBBB", data) - checksum, end = unpack_from("HB", data, length + 4) + def __enter__(self): + if self.read_serial_data_llt(writeCmd(REG_ENTER_FACTORY, CMD_ENTER_FACTORY_MODE)): + self.factory_mode = True + + def __exit__(self, type, value, traceback): + cmd_value = CMD_EXIT_AND_SAVE_FACTORY_MODE if self.writable else CMD_EXIT_FACTORY_MODE + if self.factory_mode: + if not self.read_serial_data_llt(writeCmd(REG_EXIT_FACTORY, cmd_value)): + logger.error(">>> ERROR: Unable to exit factory mode.") + else: + self.factory_mode = False + self.writable = False + + def eeprom(self, writable=False): + self.writable = writable + return self - if end == 119: - return data[4 : length + 4] - else: - logger.error(">>> ERROR: Incorrect Reply") - return False diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index fa4b38da..5da57d99 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -7,8 +7,7 @@ from utils import logger from struct import unpack_from from bleak import BleakClient, BleakScanner, BLEDevice -from bms.lltjbd import LltJbdProtection, LltJbd - +from bms.lltjbd import LltJbdProtection, LltJbd, checksum BLE_SERVICE_UUID = "0000ff00-0000-1000-8000-00805f9b34fb" BLE_CHARACTERISTICS_TX_UUID = "0000ff02-0000-1000-8000-00805f9b34fb" @@ -156,21 +155,10 @@ def read_serial_data_llt(self, command): if not self.bt_loop: return False data = asyncio.run(self.async_read_serial_data_llt(command)) - if not data: - return False - - start, flag, command_ret, length = unpack_from("BBBB", data) - checksum, end = unpack_from("HB", data, length + 4) - - if end == 119: - return data[4 : length + 4] - else: - logger.error(">>> ERROR: Incorrect Reply") - return False + return self.validate_packet(data) -""" -async def test_LltJbd_Ble(): +if __name__ == "__main__": import sys bat = LltJbd_Ble("Foo", -1, sys.argv[1]) @@ -178,8 +166,4 @@ async def test_LltJbd_Ble(): logger.error(">>> ERROR: Unable to connect") else: bat.refresh_data() - - -if __name__ == "__main__": - test_LltJbd_Ble() -""" + bat.get_settings() From ac674fcf74ceab681227ff83b1cc721ae0015697 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 14:46:20 +0200 Subject: [PATCH 186/209] clean-up --- etc/dbus-serialbattery/bms/lltjbd_ble.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index 5da57d99..ee706b67 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -5,9 +5,8 @@ import threading from typing import Union, Optional from utils import logger -from struct import unpack_from from bleak import BleakClient, BleakScanner, BLEDevice -from bms.lltjbd import LltJbdProtection, LltJbd, checksum +from bms.lltjbd import LltJbdProtection, LltJbd BLE_SERVICE_UUID = "0000ff00-0000-1000-8000-00805f9b34fb" BLE_CHARACTERISTICS_TX_UUID = "0000ff02-0000-1000-8000-00805f9b34fb" From 0902d590fc33325c91fadbd04558a4a978346abb Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 14:46:46 +0200 Subject: [PATCH 187/209] Prototype primitive SoC calibration --- etc/dbus-serialbattery/bms/lltjbd.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 3f33cd97..acd2e24b 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -75,7 +75,7 @@ REG_CAP_50 = 0x44 REG_CAP_30 = 0x45 REG_CAP_10 = 0x46 -REG_CAP2_100 = 0x47 +# REG_CAP2_100 = 0x47 # [0x48, 0x9F] - 87 registers @@ -212,6 +212,9 @@ def __init__(self, port, baud, address): self.protection = LltJbdProtection() self.type = self.BATTERYTYPE self._product_name: str = "" + self.has_settings = 0 + self.reset_soc = 100 + self.soc_to_set = None self.factory_mode = False self.writable = False @@ -257,6 +260,28 @@ def get_settings(self): return True + def reset_soc_callback(self, path, value): + if value is None: + return False + + if value < 0 or value > 100: + return False + + self.reset_soc = value + self.soc_to_set = value + return True + + def write_soc(self): + if self.soc_to_set is None or self.soc_to_set != 100 or not self.voltage: + return False + logger.info(f"write soc {self.soc_to_set}%") + self.soc_to_set = None # Reset value, so we will set it only once + # TODO implement logic to map current pack readings into + # REG_CAP_100, REG_CAP_90, REG_CAP_80, REG_CAP_70, REG_CAP_60, ... + with self.eeprom(writable=True): + pack_voltage = struct.pack(">H", int(self.voltage * 10)) + self.read_serial_data_llt(writeCmd(REG_CAP_100, pack_voltage)) + def refresh_data(self): result = self.read_gen_data() result = result and self.read_cell_data() From 2b2bdfd353c4b84f990bb09c816c82509b7db276 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 15:05:28 +0200 Subject: [PATCH 188/209] Sync Temp 3 / 4 with config.default.ini --- etc/dbus-serialbattery/battery.py | 27 +++++++++++++++-------- etc/dbus-serialbattery/config.default.ini | 6 +++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 95a67e79..108d4478 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -827,16 +827,25 @@ def get_temperatures(self) -> Union[List[float], None]: def get_temp(self) -> Union[float, None]: try: - temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] - n = len(temps) - if not temps or n == 0: - return None - data = sorted(temps) - if n % 2 == 1: - return data[n // 2] + if utils.TEMP_BATTERY == 1: + return self.temp1 + elif utils.TEMP_BATTERY == 2: + return self.temp2 + elif utils.TEMP_BATTERY == 3: + return self.temp3 + elif utils.TEMP_BATTERY == 4: + return self.temp4 else: - i = n // 2 - return (data[i - 1] + data[i]) / 2 + temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + n = len(temps) + if not temps or n == 0: + return None + data = sorted(temps) + if n % 2 == 1: + return data[n // 2] + else: + i = n // 2 + return (data[i - 1] + data[i]) / 2 except TypeError: return None diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 72d81a37..e9b4f403 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -199,10 +199,12 @@ MIDPOINT_ENABLE = False ; Battery temperature -; Specifiy how the battery temperature is assembled -; 0 Get mean of temperature sensor 1 and temperature sensor 2 +; Specify how the battery temperature is assembled +; 0 Get mean of temperature sensor 1 to sensor 4 ; 1 Get only temperature from temperature sensor 1 ; 2 Get only temperature from temperature sensor 2 +; 3 Get only temperature from temperature sensor 3 +; 4 Get only temperature from temperature sensor 4 TEMP_BATTERY = 0 ; Temperature sensor 1 name From df12cbbbf8bf8ccd3f5d1d8f322de166202e3e58 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 15:08:39 +0200 Subject: [PATCH 189/209] black lint --- etc/dbus-serialbattery/battery.py | 30 +++++++++++++++--- etc/dbus-serialbattery/bms/lltjbd.py | 47 +++++++++++++++++++--------- etc/dbus-serialbattery/dbushelper.py | 4 +-- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 108d4478..20f16586 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -836,7 +836,11 @@ def get_temp(self) -> Union[float, None]: elif utils.TEMP_BATTERY == 4: return self.temp4 else: - temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] n = len(temps) if not temps or n == 0: return None @@ -851,7 +855,11 @@ def get_temp(self) -> Union[float, None]: def get_min_temp(self) -> Union[float, None]: try: - temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] if not temps: return None return min(temps) @@ -860,7 +868,11 @@ def get_min_temp(self) -> Union[float, None]: def get_min_temp_id(self) -> Union[str, None]: try: - temps = [(t, i) for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) if t is not None] + temps = [ + (t, i) + for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) + if t is not None + ] if not temps: return None index = min(temps)[1] @@ -877,7 +889,11 @@ def get_min_temp_id(self) -> Union[str, None]: def get_max_temp(self) -> Union[float, None]: try: - temps = [t for t in [self.temp1, self.temp2, self.temp3, self.temp4] if t is not None] + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] if not temps: return None return max(temps) @@ -886,7 +902,11 @@ def get_max_temp(self) -> Union[float, None]: def get_max_temp_id(self) -> Union[str, None]: try: - temps = [(t, i) for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) if t is not None] + temps = [ + (t, i) + for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) + if t is not None + ] if not temps: return None index = max(temps)[1] diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index acd2e24b..8272bc71 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -137,9 +137,9 @@ REG_RESET = 0xE3 # Protocol commands -CMD_ENTER_FACTORY_MODE = b'\x56\x78' -CMD_EXIT_FACTORY_MODE = b'\x00\x00' -CMD_EXIT_AND_SAVE_FACTORY_MODE = b'\x28\x28' +CMD_ENTER_FACTORY_MODE = b"\x56\x78" +CMD_EXIT_FACTORY_MODE = b"\x00\x00" +CMD_EXIT_AND_SAVE_FACTORY_MODE = b"\x28\x28" def checksum(payload): @@ -150,7 +150,7 @@ def cmd(op, reg, data): payload = [reg, len(data)] + list(data) chksum = checksum(payload) data = [0xDD, op] + payload + [chksum, 0x77] - format = f'>BB{len(payload)}BHB' + format = f">BB{len(payload)}BHB" return struct.pack(format, *data) @@ -253,10 +253,14 @@ def get_settings(self): with self.eeprom(writable=False): charge_over_current = self.read_serial_data_llt(readCmd(REG_CHGOC)) if charge_over_current: - self.max_battery_charge_current = float(unpack_from(">h", charge_over_current)[0] / 100.0) + self.max_battery_charge_current = float( + unpack_from(">h", charge_over_current)[0] / 100.0 + ) discharge_over_current = self.read_serial_data_llt(readCmd(REG_DSGOC)) if discharge_over_current: - self.max_battery_discharge_current = float(unpack_from(">h", discharge_over_current)[0] / -100.0) + self.max_battery_discharge_current = float( + unpack_from(">h", discharge_over_current)[0] / -100.0 + ) return True @@ -400,7 +404,9 @@ def read_gen_data(self): self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) - self.hardware_version = float(str(version >> 4 & 0x0F) + "." + str(version & 0x0F)) + self.hardware_version = float( + str(version >> 4 & 0x0F) + "." + str(version & 0x0F) + ) self.to_fet_bits(fet) self.to_protection_bits(protection) self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count @@ -450,23 +456,31 @@ def validate_packet(data): start, op, status, payload_length = unpack_from("BBBB", data) if start != 0xDD: - logger.error(">>> ERROR: Invalid response packet. Expected begin packet character 0xDD") + logger.error( + ">>> ERROR: Invalid response packet. Expected begin packet character 0xDD" + ) if status != 0x0: logger.warn(">>> WARN: BMS rejected request. Status " + status) return False if len(data) != payload_length + 7: - logger.error(">>> ERROR: BMS send insufficient data. Received " + str(len(data)) + " expected " + str( - payload_length + 7)) + logger.error( + ">>> ERROR: BMS send insufficient data. Received " + + str(len(data)) + + " expected " + + str(payload_length + 7) + ) return False chk_sum, end = unpack_from(">HB", data, payload_length + 4) if end != 0x77: - logger.error(">>> ERROR: Incorrect Reply. Expected end packet character 0x77") + logger.error( + ">>> ERROR: Incorrect Reply. Expected end packet character 0x77" + ) return False if chk_sum != checksum(data[2:-3]): logger.error(">>> ERROR: Invalid checksum.") return False - payload = data[4: payload_length + 4] + payload = data[4 : payload_length + 4] return payload @@ -477,11 +491,15 @@ def read_serial_data_llt(self, command): return self.validate_packet(data) def __enter__(self): - if self.read_serial_data_llt(writeCmd(REG_ENTER_FACTORY, CMD_ENTER_FACTORY_MODE)): + if self.read_serial_data_llt( + writeCmd(REG_ENTER_FACTORY, CMD_ENTER_FACTORY_MODE) + ): self.factory_mode = True def __exit__(self, type, value, traceback): - cmd_value = CMD_EXIT_AND_SAVE_FACTORY_MODE if self.writable else CMD_EXIT_FACTORY_MODE + cmd_value = ( + CMD_EXIT_AND_SAVE_FACTORY_MODE if self.writable else CMD_EXIT_FACTORY_MODE + ) if self.factory_mode: if not self.read_serial_data_llt(writeCmd(REG_EXIT_FACTORY, cmd_value)): logger.error(">>> ERROR: Unable to exit factory mode.") @@ -492,4 +510,3 @@ def __exit__(self, type, value, traceback): def eeprom(self, writable=False): self.writable = writable return self - diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index 45a4705e..0cca3187 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -118,9 +118,7 @@ def setup_vedbus(self): # Create the mandatory objects self._dbusservice.add_path("/DeviceInstance", self.instance) self._dbusservice.add_path("/ProductId", 0x0) - self._dbusservice.add_path( - "/ProductName", self.battery.product_name() - ) + self._dbusservice.add_path("/ProductName", self.battery.product_name()) self._dbusservice.add_path("/FirmwareVersion", str(utils.DRIVER_VERSION)) self._dbusservice.add_path("/HardwareVersion", self.battery.hardware_version) self._dbusservice.add_path("/Connected", 1) From 6e334582e2087d733480413cd58a0871790899ab Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 15:25:17 +0200 Subject: [PATCH 190/209] Jbd uses digit like mac address 70:3E:97:D2:F4:D0 Fix with ble prefix: ValueError: Invalid bus name 'com.victronenergy.battery.703e97d2f4d0': a digit may not follow '.' except in a unique name starting with ':' --- etc/dbus-serialbattery/bms/lltjbd_ble.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index ee706b67..f0f916c7 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -19,7 +19,7 @@ class LltJbd_Ble(LltJbd): BATTERYTYPE = "LltJbd_Ble" def __init__(self, port: Optional[str], baud: Optional[int], address: str): - super(LltJbd_Ble, self).__init__(address.replace(":", "").lower(), -1, address) + super(LltJbd_Ble, self).__init__("ble" + address.replace(":", "").lower(), -1, address) self.address = address self.protection = LltJbdProtection() From 6da9e3d269bd8572d9beb9644554abb3a0bdf5b0 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 15:25:30 +0200 Subject: [PATCH 191/209] Jbd uses digit like mac address 70:3E:97:D2:F4:D0 Fix with ble prefix: ValueError: Invalid bus name 'com.victronenergy.battery.703e97d2f4d0': a digit may not follow '.' except in a unique name starting with ':' --- etc/dbus-serialbattery/bms/lltjbd_ble.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index f0f916c7..ed54a2f2 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -19,7 +19,9 @@ class LltJbd_Ble(LltJbd): BATTERYTYPE = "LltJbd_Ble" def __init__(self, port: Optional[str], baud: Optional[int], address: str): - super(LltJbd_Ble, self).__init__("ble" + address.replace(":", "").lower(), -1, address) + super(LltJbd_Ble, self).__init__( + "ble" + address.replace(":", "").lower(), -1, address + ) self.address = address self.protection = LltJbdProtection() From ef5380b16b6923389036a5e638866ad327183864 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Fri, 19 May 2023 17:50:01 +0200 Subject: [PATCH 192/209] Fix reading settings --- etc/dbus-serialbattery/bms/lltjbd.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 8272bc71..4c091aff 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -233,10 +233,12 @@ def test_connection(self): # Return True if success, False for failure result = False try: - result = self.read_hardware_data() + result = self.get_settings() + if result: + result = result and self.read_hardware_data() # get first data to show in startup log if result: - self.refresh_data() + result = result and self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False @@ -247,7 +249,8 @@ def product_name(self) -> str: return self._product_name def get_settings(self): - self.read_gen_data() + if not self.read_gen_data(): + return False self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT with self.eeprom(writable=False): From 3c21953202e2aa91e645c9b1b15d7a8944be1117 Mon Sep 17 00:00:00 2001 From: Oleg Gurevich Date: Sat, 20 May 2023 20:23:51 +0200 Subject: [PATCH 193/209] Limit control voltage to max cell voltage --- etc/dbus-serialbattery/battery.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 767be09d..2b8c8d9d 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -241,15 +241,19 @@ def manage_charge_voltage_linear(self) -> None: ): self.linear_cvl_last_set = int(time()) - # Keep penalty above min battery voltage + # Keep penalty above min battery voltage and below max battery voltage self.control_voltage = round( - max( - voltageSum - penaltySum, - utils.MIN_CELL_VOLTAGE * self.cell_count, + min( + max( + voltageSum - penaltySum, + utils.MIN_CELL_VOLTAGE * self.cell_count, + ), + utils.MAX_CELL_VOLTAGE * self.cell_count ), 3, ) + self.charge_mode = ( "Bulk dynamic" # + " (vS: " From f7d61c7ef90c673831071b40c393ec8793cea9b3 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Sun, 21 May 2023 08:27:34 +0200 Subject: [PATCH 194/209] Fix issue with bluetooth restarts (e.g. cronjob) --- etc/dbus-serialbattery/bms/lltjbd_ble.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index ed54a2f2..7cf3fbdc 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -50,9 +50,14 @@ def on_disconnect(self, client): logger.info("BLE client disconnected") async def bt_main_loop(self): - self.device = await BleakScanner.find_device_by_address( - self.address, cb=dict(use_bdaddr=True) - ) + try: + self.device = await BleakScanner.find_device_by_address( + self.address, cb=dict(use_bdaddr=True) + ) + except Exception as e: + logger.error(">>> ERROR: Bluetooth stack failed.", e) + self.device = None + await asyncio.sleep(0.5) if not self.device: self.run = False @@ -155,8 +160,12 @@ async def async_read_serial_data_llt(self, command): def read_serial_data_llt(self, command): if not self.bt_loop: return False - data = asyncio.run(self.async_read_serial_data_llt(command)) - return self.validate_packet(data) + try: + data = asyncio.run(self.async_read_serial_data_llt(command)) + return self.validate_packet(data) + except Exception as e: + logger.error(">>> ERROR: No reply - returning", e) + return False if __name__ == "__main__": From 3811281ef98a379d16b20eee93e609c7088522b8 Mon Sep 17 00:00:00 2001 From: Bernd <6627385+transistorgit@users.noreply.github.com> Date: Sun, 21 May 2023 21:23:56 +0200 Subject: [PATCH 195/209] Rework serial parser (#12) much cleaner code, as it is optimised for the daly protocol --- etc/dbus-serialbattery/bms/daly.py | 418 +++++++------------ etc/dbus-serialbattery/qml/PageLynxIonIo.qml | 1 + 2 files changed, 161 insertions(+), 258 deletions(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 54226dac..8555367b 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -21,7 +21,7 @@ def __init__(self, port, baud, address): self.poll_interval = 1000 self.type = self.BATTERYTYPE self.has_settings = 1 - self.reset_soc = 100 + self.reset_soc = 0 self.soc_to_set = None self.runtime = 0 # TROUBLESHOOTING for no reply errors self.trigger_force_disable_discharge = None @@ -85,12 +85,13 @@ def refresh_data(self): try: with open_serial_port(self.port, self.baud_rate) as ser: result = self.read_soc_data(ser) + self.reset_soc = self.soc if self.soc else 0 if self.runtime > 0.200: # TROUBLESHOOTING for no reply errors logger.info( " |- refresh_data: read_soc_data - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -100,7 +101,7 @@ def refresh_data(self): " |- refresh_data: read_fed_data - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -110,7 +111,7 @@ def refresh_data(self): " |- refresh_data: read_cell_voltage_range_data - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -120,7 +121,7 @@ def refresh_data(self): " |- refresh_data: write_soc_and_datetime - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -130,7 +131,7 @@ def refresh_data(self): " |- refresh_data: read_alarm_data - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -140,7 +141,7 @@ def refresh_data(self): " |- refresh_data: read_temperature_range_data - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -150,7 +151,7 @@ def refresh_data(self): " |- refresh_data: read_balance_state - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -160,7 +161,7 @@ def refresh_data(self): " |- refresh_data: read_cells_volts - result: " + str(result) + " - runtime: " - + str(self.runtime) + + str(f"{self.runtime:.1f}") + "s" ) @@ -174,7 +175,7 @@ def refresh_data(self): return result def read_status_data(self, ser): - status_data = self.read_serial_data_daly(ser, self.command_status) + status_data = self.request_data(ser, self.command_status) # check if connection success if status_data is False: logger.warning("No data received in read_status_data()") @@ -208,7 +209,7 @@ def read_soc_data(self, ser): triesValid = 2 while triesValid > 0: triesValid -= 1 - soc_data = self.read_serial_data_daly(ser, self.command_soc) + soc_data = self.request_data(ser, self.command_soc) # check if connection success if soc_data is False: continue @@ -229,7 +230,7 @@ def read_soc_data(self, ser): return False def read_alarm_data(self, ser): - alarm_data = self.read_serial_data_daly(ser, self.command_alarm) + alarm_data = self.request_data(ser, self.command_alarm) # check if connection success if alarm_data is False: logger.warning("No data received in read_alarm_data()") @@ -339,76 +340,59 @@ def read_alarm_data(self, ser): return True def read_cells_volts(self, ser): - if self.cell_count is not None: - buffer = bytearray(self.command_base) - buffer[1] = self.command_address[0] # Always serial 40 or 80 - buffer[2] = self.command_cell_volts[0] - buffer[12] = sum(buffer[:12]) & 0xFF - - # logger.info(f"{bytes(buffer).hex()}") - - if (int(self.cell_count) % 3) == 0: - maxFrame = int(self.cell_count / 3) - else: - maxFrame = int(self.cell_count / 3) + 1 - lenFixed = ( - maxFrame * 13 - ) # 0xA5, 0x01, 0x95, 0x08 + 1 byte frame + 6 byte data + 1byte reserved + chksum - - cells_volts_data = self.read_serialport_data( - ser, buffer, self.LENGTH_POS, 0, lenFixed - ) - if cells_volts_data is False: - logger.warning("No data received in read_cells_volts()") - return False + if self.cell_count is None: + return True + + # calculate how many sentences we will receive + # in each sentence, the bms will send 3 cell voltages + # so for a 4s, we will receive 2 sentences + if (int(self.cell_count) % 3) == 0: + sentences_expected = int(self.cell_count / 3) + else: + sentences_expected = int(self.cell_count / 3) + 1 + + cells_volts_data = self.request_data( + ser, self.command_cell_volts, sentences_to_receive=sentences_expected + ) + + if cells_volts_data is False: + logger.debug( + "No or invalid data has been received in read_cells_volts()" + ) # just debug level, as there are DALY BMS that send broken packages occasionally + return False + + frameCell = [0, 0, 0] + lowMin = utils.MIN_CELL_VOLTAGE / 2 + frame = 0 + + if len(self.cells) != self.cell_count: + # init the numbers of cells + self.cells = [] + for idx in range(self.cell_count): + self.cells.append(Cell(True)) - frameCell = [0, 0, 0] - lowMin = utils.MIN_CELL_VOLTAGE / 2 - frame = 0 - bufIdx = 0 - - if len(self.cells) != self.cell_count: - # init the numbers of cells - self.cells = [] - for idx in range(self.cell_count): - self.cells.append(Cell(True)) - - # logger.warning("data " + bytes(cells_volts_data).hex()) - - while bufIdx <= len(cells_volts_data) - ( - 4 + 8 + 1 - ): # we at least need 13 bytes to extract the identifiers + 8 bytes payload + checksum - b1, b2, b3, b4 = unpack_from(">BBBB", cells_volts_data, bufIdx) - if b1 == 0xA5 and b2 == 0x01 and b3 == 0x95 and b4 == 0x08: - ( - frame, - frameCell[0], - frameCell[1], - frameCell[2], - _, - chk, - ) = unpack_from(">BhhhBB", cells_volts_data, bufIdx + 4) - if sum(cells_volts_data[bufIdx : bufIdx + 12]) & 0xFF != chk: - logger.warning("bad cell voltages checksum") - else: - for idx in range(3): - cellnum = ( - (frame - 1) * 3 - ) + idx # daly is 1 based, driver 0 based - if cellnum >= self.cell_count: - break - cellVoltage = frameCell[idx] / 1000 - self.cells[cellnum].voltage = ( - None if cellVoltage < lowMin else cellVoltage - ) - bufIdx += 13 # BBBBBhhhBB -> 13 byte - else: - bufIdx += 1 # step through buffer to find valid start - logger.warning("bad cell voltages header") + # logger.warning("data " + bytes(cells_volts_data).hex()) + + # from each of the received sentences, read up to 3 voltages + for i in range(sentences_expected): + ( + frame, + frameCell[0], + frameCell[1], + frameCell[2], + ) = unpack_from(">Bhhh", cells_volts_data, 8 * i) + for idx in range(3): + cellnum = ((frame - 1) * 3) + idx # daly is 1 based, driver 0 based + if cellnum >= self.cell_count: + break # ignore possible unused bytes of last sentence + cellVoltage = frameCell[idx] / 1000 + self.cells[cellnum].voltage = ( + None if cellVoltage < lowMin else cellVoltage + ) return True def read_cell_voltage_range_data(self, ser): - minmax_data = self.read_serial_data_daly(ser, self.command_minmax_cell_volts) + minmax_data = self.request_data(ser, self.command_minmax_cell_volts) # check if connection success if minmax_data is False: logger.warning("No data received in read_cell_voltage_range_data()") @@ -429,7 +413,7 @@ def read_cell_voltage_range_data(self, ser): return True def read_balance_state(self, ser): - balance_data = self.read_serial_data_daly(ser, self.command_cell_balance) + balance_data = self.request_data(ser, self.command_cell_balance) # check if connection success if balance_data is False: logger.debug("No data received in read_balance_state()") @@ -445,7 +429,7 @@ def read_balance_state(self, ser): return True def read_temperature_range_data(self, ser): - minmax_data = self.read_serial_data_daly(ser, self.command_minmax_temp) + minmax_data = self.request_data(ser, self.command_minmax_temp) # check if connection success if minmax_data is False: logger.debug("No data received in read_temperature_range_data()") @@ -457,7 +441,7 @@ def read_temperature_range_data(self, ser): return True def read_fed_data(self, ser): - fed_data = self.read_serial_data_daly(ser, self.command_fet) + fed_data = self.request_data(ser, self.command_fet) # check if connection success if fed_data is False: logger.debug("No data received in read_fed_data()") @@ -475,7 +459,7 @@ def read_fed_data(self, ser): # new def read_capacity(self, ser): - capa_data = self.read_serial_data_daly(ser, self.command_rated_params) + capa_data = self.request_data(ser, self.command_rated_params) # check if connection success if capa_data is False: logger.warning("No data received in read_capacity()") @@ -490,7 +474,7 @@ def read_capacity(self, ser): # new def read_production_date(self, ser): - production = self.read_serial_data_daly(ser, self.command_batt_details) + production = self.request_data(ser, self.command_batt_details) # check if connection success if production is False: logger.warning("No data received in read_production_date()") @@ -502,39 +486,19 @@ def read_production_date(self, ser): # new def read_battery_code(self, ser): - lenFixed = ( - 5 * 13 - ) # batt code field is 35 bytes and we transfer 7 bytes in each telegram - data = self.read_serialport_data( - ser, - self.generate_command(self.command_batt_code), - self.LENGTH_POS, - 0, - lenFixed, - ) + data = self.request_data(ser, self.command_batt_code, sentences_to_receive=5) if data is False: logger.warning("No data received in read_battery_code()") return False - bufIdx = 0 battery_code = "" # logger.warning("data " + bytes(cells_volts_data).hex()) - while ( - bufIdx <= len(data) - 13 - ): # we at least need 13 bytes to extract the identifiers + 8 bytes payload + checksum - b1, b2, b3, b4 = unpack_from(">BBBB", data, bufIdx) - if b1 == 0xA5 and b2 == 0x01 and b3 == 0x57 and b4 == 0x08: - _, part, chk = unpack_from(">B7sB", data, bufIdx + 4) - if sum(data[bufIdx : bufIdx + 12]) & 0xFF != chk: - logger.warning( - "bad battery code checksum" - ) # use string anyhow, just warn - battery_code += part.decode("utf-8") - bufIdx += 13 # BBBBB7sB -> 13 byte - else: - bufIdx += 1 # step through buffer to find valid start - logger.warning("bad battery code header") + for i in range(5): + nr, part = unpack_from(">B7s", data, i * 8) + if nr != i + 1: + logger.warning("bad battery code index") # use string anyhow, just warn + battery_code += part.decode("utf-8") if battery_code != "": self.custom_field = sub( @@ -549,145 +513,6 @@ def read_battery_code(self, ser): ) return True - def generate_command(self, command): - buffer = bytearray(self.command_base) - buffer[1] = self.command_address[0] # Always serial 40 or 80 - buffer[2] = command[0] - buffer[12] = sum(buffer[:12]) & 0xFF # checksum calc - return buffer - - def read_serial_data_daly(self, ser, command): - data = self.read_serialport_data( - ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK - ) - if data is False: - # sleep 100 ms and retry. - sleep(0.100) - data = self.read_serialport_data( - ser, self.generate_command(command), self.LENGTH_POS, self.LENGTH_CHECK - ) - if data is False: - logger.info("No reply to cmd " + bytes(command).hex()) - return False - else: - logger.info(" |- Error cleared, received data after one retry.") - - if len(data) <= 12: - logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False - - # search sentence start - try: - idx = data.index(0xA5) - except ValueError: - logger.debug( - "No Sentence Start found for reply to cmd " + bytes(command).hex() - ) - return False - - if len(data[idx:]) <= 12: - logger.debug("Too short reply to cmd " + bytes(command).hex()) - return False - - if data[12 + idx] != sum(data[idx : 12 + idx]) & 0xFF: - logger.debug("Bad checksum in reply to cmd " + bytes(command).hex()) - return False - - _, _, _, length = unpack_from(">BBBB", data, idx) - - if length == 8: - return data[4 + idx : length + 4 + idx] - else: - logger.debug( - ">>> ERROR: Incorrect Reply to CMD " - + bytes(command).hex() - + ": 0x" - + bytes(data).hex() - ) - return False - - # Read data from previously opened serial port - def read_serialport_data( - self, - ser, - command, - length_pos, - length_check, - length_fixed=None, - length_size=None, - ): - try: - # wait shortly, else the Daly is not ready and throws a lot of no reply errors - # if you see a lot of errors, try to increase in steps of 0.005 - sleep(0.020) - - time_run = 0 - time_start = time() - ser.flushOutput() - ser.flushInput() - ser.write(command) - - length_byte_size = 1 - if length_size is not None: - if length_size.upper() == "H": - length_byte_size = 2 - elif length_size.upper() == "I" or length_size.upper() == "L": - length_byte_size = 4 - - toread = ser.inWaiting() - - while toread < (length_pos + length_byte_size): - sleep(0.005) - toread = ser.inWaiting() - time_run = time() - time_start - if time_run > 0.500: - self.runtime = time_run - logger.error(">>> ERROR: No reply - returning") - return False - - # logger.info('serial data toread ' + str(toread)) - res = ser.read(toread) - if length_fixed is not None: - length = length_fixed - else: - if len(res) < (length_pos + length_byte_size): - logger.error( - ">>> ERROR: No reply - returning [len:" + str(len(res)) + "]" - ) - return False - length_size = length_size if length_size is not None else "B" - length = unpack_from(">" + length_size, res, length_pos)[0] - - data = bytearray(res) - - packetlen = ( - length_fixed - if length_fixed is not None - else length_pos + length_byte_size + length + length_check - ) - while len(data) < packetlen: - res = ser.read(packetlen - len(data)) - data.extend(res) - sleep(0.005) - time_run = time() - time_start - if time_run > 0.500: - self.runtime = time_run - logger.error( - ">>> ERROR: No reply - returning [len:" - + str(len(data)) - + "/" - + str(length + length_check) - + "]" - ) - return False - - self.runtime = time_run - return data - - except Exception as e: - logger.error(e) - return False - def reset_soc_callback(self, path, value): if value is None: return False @@ -727,9 +552,13 @@ def write_soc_and_datetime(self, ser): logger.info(f"write soc {self.soc_to_set}%") self.soc_to_set = None # Reset value, so we will set it only once - reply = self.read_serialport_data(ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK) + time_start = time() + ser.flushOutput() + ser.flushInput() + ser.write(cmd) - if reply[4] != 1: + reply = self.read_sentence(ser, self.command_set_soc) + if reply[0] != 1: logger.error("write soc failed") return True @@ -778,11 +607,12 @@ def write_charge_discharge_mos(self, ser): f"write force disable charging: {'true' if self.trigger_force_disable_charge else 'false'}" ) self.trigger_force_disable_charge = None + ser.flushOutput() + ser.flushInput() + ser.write(cmd) - reply = self.read_serialport_data( - ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK - ) - if reply is False or reply[4] != cmd[4]: + reply = self.read_sentence(ser, self.command_disable_charge_mos) + if reply is False or reply[0] != cmd[4]: logger.error("write force disable charge/discharge failed") return False @@ -794,11 +624,83 @@ def write_charge_discharge_mos(self, ser): f"write force disable discharging: {'true' if self.trigger_force_disable_discharge else 'false'}" ) self.trigger_force_disable_discharge = None + ser.flushOutput() + ser.flushInput() + ser.write(cmd) - reply = self.read_serialport_data( - ser, cmd, self.LENGTH_POS, self.LENGTH_CHECK - ) - if reply is False or reply[4] != cmd[4]: + reply = self.read_sentence(ser, self.command_disable_discharge_mos) + if reply is False or reply[0] != cmd[4]: logger.error("write force disable charge/discharge failed") return False return True + + def generate_command(self, command): + buffer = bytearray(self.command_base) + buffer[1] = self.command_address[0] # Always serial 40 or 80 + buffer[2] = command[0] + buffer[12] = sum(buffer[:12]) & 0xFF # checksum calc + return buffer + + def request_data(self, ser, command, sentences_to_receive=1): + # wait shortly, else the Daly is not ready and throws a lot of no reply errors + # if you see a lot of errors, try to increase in steps of 0.005 + sleep(0.020) + + self.runtime = 0 + time_start = time() + ser.flushOutput() + ser.flushInput() + ser.write(self.generate_command(command)) + + reply = bytearray() + for i in range(sentences_to_receive): + next = self.read_sentence(ser, command) + if not next: + logger.info(f"request_data: bad reply no. {i}") + return False + reply += next + self.runtime = time() - time_start + return reply + + def read_sentence(self, ser, expected_reply, timeout=0.5): + """read one 13 byte sentence from daly smart bms. + return false if less than 13 bytes received in timeout secs, or frame errors occured + return received datasection as bytearray else + """ + time_start = time() + + reply = ser.read_until(b"\xA5") + if not reply or b"\xA5" not in reply: + logger.error( + f"read_sentence {bytes(expected_reply).hex()}: no sentence start received" + ) + return False + + idx = reply.index(b"\xA5") + reply = reply[idx:] + toread = ser.inWaiting() + while toread < 12: + sleep((12 - toread) * 0.001) + toread = ser.inWaiting() + time_run = time() - time_start + if time_run > timeout: + logger.warning(f"read_sentence {bytes(expected_reply).hex()}: timeout") + return False + + reply += ser.read(12) + _, id, cmd, length = unpack_from(">BBBB", reply) + + # logger.info(f"reply: {bytes(reply).hex()}") # debug + + if id != 1 or length != 8 or cmd != expected_reply[0]: + logger.error(f"read_sentence {bytes(expected_reply).hex()}: wrong header") + return False + + chk = unpack_from(">B", reply, 12)[0] + if sum(reply[:12]) & 0xFF != chk: + logger.warning( + f"read_sentence {bytes(expected_reply).hex()}: wrong checksum" + ) + return False + + return reply[4:12] diff --git a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml index 21bfedf4..9d0ff4c4 100644 --- a/etc/dbus-serialbattery/qml/PageLynxIonIo.qml +++ b/etc/dbus-serialbattery/qml/PageLynxIonIo.qml @@ -88,5 +88,6 @@ MbPage { MbOption{description: qsTr("Active"); value: 1} ] } + } } From a69118e8df8166f4633466768be10ed2c9d0941a Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 21 May 2023 21:26:17 +0200 Subject: [PATCH 196/209] updated changelog --- CHANGELOG.md | 1 + etc/dbus-serialbattery/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad28b18d..68bf2917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ * Changed: Bash output by @mr-manuel * Changed: Daly BMS - Fixed BMS alerts by @mr-manuel * Changed: Daly BMS - Improved driver stability by @transistorgit & @mr-manuel +* Changed: Daly BMS - Reworked serial parser by @transistorgit * Changed: Default config file by @ppuetsch * Added missing descriptions to make it much clearer to understand by @mr-manuel * Changed name from `default_config.ini` to `config.default.ini` https://github.com/Louisvdw/dbus-serialbattery/pull/412#issuecomment-1434287942 by @mr-manuel diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index e36782da..35ffa370 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -35,7 +35,7 @@ def _get_list_from_config( # if not specified: baud = 9600 # Constants - Need to dynamically get them in future -DRIVER_VERSION = "1.0.20230518dev" +DRIVER_VERSION = "1.0.20230521dev" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 96d7c3861cd2d025e35187370571d568d7b66ea9 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 21 May 2023 21:32:04 +0200 Subject: [PATCH 197/209] fix black lint error --- etc/dbus-serialbattery/battery.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 2b8c8d9d..04f0258d 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -248,12 +248,11 @@ def manage_charge_voltage_linear(self) -> None: voltageSum - penaltySum, utils.MIN_CELL_VOLTAGE * self.cell_count, ), - utils.MAX_CELL_VOLTAGE * self.cell_count + utils.MAX_CELL_VOLTAGE * self.cell_count, ), 3, ) - self.charge_mode = ( "Bulk dynamic" # + " (vS: " From f7d76212ce7ae9f0df7964ee71f289c9c179b75a Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 21 May 2023 21:47:05 +0200 Subject: [PATCH 198/209] fixed black lint error --- etc/dbus-serialbattery/bms/daly.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py index 8555367b..b304b283 100644 --- a/etc/dbus-serialbattery/bms/daly.py +++ b/etc/dbus-serialbattery/bms/daly.py @@ -552,7 +552,6 @@ def write_soc_and_datetime(self, ser): logger.info(f"write soc {self.soc_to_set}%") self.soc_to_set = None # Reset value, so we will set it only once - time_start = time() ser.flushOutput() ser.flushInput() ser.write(cmd) From e17ebab33ca849808cdd70538bdea12f61febcfd Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 22 May 2023 10:19:25 +0200 Subject: [PATCH 199/209] added infos to battery template --- etc/dbus-serialbattery/bms/battery_template.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/bms/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py index e6148e65..e6cbd6a6 100644 --- a/etc/dbus-serialbattery/bms/battery_template.py +++ b/etc/dbus-serialbattery/bms/battery_template.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- # NOTES -# Please also update the feature comparison table, if you are adding a new BMS -# https://louisvdw.github.io/dbus-serialbattery/general/features/#bms-feature-comparison +# Please see "Add/Request a new BMS" https://louisvdw.github.io/dbus-serialbattery/general/supported-bms#add-by-opening-a-pull-request +# in the documentation for a checklist what you have to do, when adding a new BMS +# avoid importing wildcards from battery import Protection, Battery, Cell from utils import is_bit_set, read_serial_data, logger import utils From 9b669e425503e7250344a4b49cf8f3c0caa3a118 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Mon, 22 May 2023 14:24:29 +0200 Subject: [PATCH 200/209] Fix issue with bluetooth restarts (e.g. cronjob) --- etc/dbus-serialbattery/bms/lltjbd_ble.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index 7cf3fbdc..de995492 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -3,6 +3,7 @@ import atexit import functools import threading +from asyncio import CancelledError from typing import Union, Optional from utils import logger from bleak import BleakClient, BleakScanner, BLEDevice @@ -163,6 +164,9 @@ def read_serial_data_llt(self, command): try: data = asyncio.run(self.async_read_serial_data_llt(command)) return self.validate_packet(data) + except CancelledError as e: + logger.error(">>> ERROR: No reply - canceled - returning", e) + return False except Exception as e: logger.error(">>> ERROR: No reply - returning", e) return False From ef376e354dc07b0fbc9a944b54d5b55d98c20935 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Mon, 22 May 2023 17:11:43 +0200 Subject: [PATCH 201/209] WIP Add func controls for balancer, chg/dsg MOS FET --- etc/dbus-serialbattery/bms/lltjbd.py | 138 ++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 4c091aff..9df6e06f 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -68,8 +68,10 @@ REG_CELL_V_DELAYS = 0x3D REG_CHGOC_DELAYS = 0x3E REG_DSGOC_DELAYS = 0x3F -REG_GPSOFF = 0x40 -REG_GPSOFF_TIME = 0x41 +# Cut-off voltage turns off GPS protection board +REG_GPS_OFF = 0x40 +# Cut-off voltage delay for GPS protection board +REG_GPS_OFF_TIME = 0x41 REG_CAP_90 = 0x42 REG_CAP_70 = 0x43 REG_CAP_50 = 0x44 @@ -141,6 +143,26 @@ CMD_EXIT_FACTORY_MODE = b"\x00\x00" CMD_EXIT_AND_SAVE_FACTORY_MODE = b"\x28\x28" +# Weak current switch function +FUNC_SW_EN = 0x0001 +# Load lock function used to disconnect the load when short circuit is required to recover +FUNC_LOAD_EN = 0x0002 +# Enable balancer function +FUNC_BALANCE_EN = 0x0004 +# Charge balance, only turn on balance when charging +FUNC_BALANCE_CHARGING_ONLY = 0x0008 +# LED power indicator function +FUNC_LED = 0x0010 +# Compatible with LED modes +FUNC_LED_NUM = 0x0020 +# With history recording +FUNC_RTC = 0x0040 +# whether it is necessary to set the range when it is currently used for FCC update +FUNC_EDV = 0x0080 +# Additional GPS protection board is connected +FUNC_GPS_EN = 0x0100 +# Enable onboard buzzer / GPS protection board buzzer? +FUNC_BUZZER_EN = 0x0200 def checksum(payload): return (0x10000 - sum(payload)) % 0x10000 @@ -217,6 +239,9 @@ def __init__(self, port, baud, address): self.soc_to_set = None self.factory_mode = False self.writable = False + self.trigger_force_disable_discharge = None + self.trigger_force_disable_charge = None + self.trigger_disable_balancer = None # degree_sign = u'\N{DEGREE SIGN}' BATTERYTYPE = "LLT/JBD" @@ -264,6 +289,8 @@ def get_settings(self): self.max_battery_discharge_current = float( unpack_from(">h", discharge_over_current)[0] / -100.0 ) + func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) + return True @@ -289,9 +316,116 @@ def write_soc(self): pack_voltage = struct.pack(">H", int(self.voltage * 10)) self.read_serial_data_llt(writeCmd(REG_CAP_100, pack_voltage)) + def force_charging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_charge = False + return True + + if value == 1: + self.trigger_force_disable_charge = True + return True + + return False + + def force_discharging_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_force_disable_discharge = False + return True + + if value == 1: + self.trigger_force_disable_discharge = True + return True + + return False + + def write_charge_discharge_mos(self): + if ( + self.trigger_force_disable_charge is None + and self.trigger_force_disable_discharge is None + ): + return False + + charge_disabled = 0 if self.charge_fet and self.control_allow_charge else 1 + if self.trigger_force_disable_charge is not None: + charge_disabled = 1 + logger.info( + f"write force disable charging: {'true' if self.trigger_force_disable_charge else 'false'}" + ) + self.trigger_force_disable_charge = None + + discharge_disabled = 0 if self.discharge_fet and self.control_allow_discharge else 1 + if self.trigger_force_disable_discharge is not None: + discharge_disabled = 1 + logger.info( + f"write force disable discharging: {'true' if self.trigger_force_disable_discharge else 'false'}" + ) + self.trigger_force_disable_discharge = None + + value = charge_disabled | (discharge_disabled << 1) + + cmd = writeCmd(REG_CTRL_MOSFET, value) + + reply = self.read_serial_data_llt(cmd) + + if reply is False: + logger.error("write force disable charge/discharge failed") + return False + + def turn_balancing_off_callback(self, path, value): + if value is None: + return False + + if value == 0: + self.trigger_disable_balancer = False + return True + + if value == 1: + self.trigger_disable_balancer = True + return True + + return False + + def write_balancer(self): + if self.trigger_disable_balancer is None: + return False + + disable_balancer = self.trigger_disable_balancer + logger.info( + f"write disable balancer: {'true' if self.trigger_disable_balancer else 'false'}" + ) + self.trigger_disable_balancer = None + new_func_config = None + + with self.eeprom(): + func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) + if func_config: + config = unpack_from(">H", func_config)[0] + balancer_enabled = config & FUNC_BALANCE_EN + # Balance is enabled, force disable OR balancer is disabled and disable force disable + if (balancer_enabled and disable_balancer) or (not balancer_enabled and not disable_balancer): + new_func_config = config ^ FUNC_BALANCE_EN + + if new_func_config: + with self.eeprom(writable=True): + reply = self.read_serial_data_llt(writeCmd(REG_FUNC_CONFIG, new_func_config)) + if reply is False: + logger.error("write force disable balancer failed") + return False + + return True + + + def refresh_data(self): result = self.read_gen_data() result = result and self.read_cell_data() + self.write_charge_discharge_mos() return result def to_protection_bits(self, byte_data): From 37a589d8aa5794e577b8b445bd8c84d086aba949 Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Mon, 22 May 2023 17:12:00 +0200 Subject: [PATCH 202/209] black formatting --- etc/dbus-serialbattery/bms/lltjbd.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 9df6e06f..2e74882e 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -164,6 +164,7 @@ # Enable onboard buzzer / GPS protection board buzzer? FUNC_BUZZER_EN = 0x0200 + def checksum(payload): return (0x10000 - sum(payload)) % 0x10000 @@ -291,7 +292,6 @@ def get_settings(self): ) func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) - return True def reset_soc_callback(self, path, value): @@ -359,7 +359,9 @@ def write_charge_discharge_mos(self): ) self.trigger_force_disable_charge = None - discharge_disabled = 0 if self.discharge_fet and self.control_allow_discharge else 1 + discharge_disabled = ( + 0 if self.discharge_fet and self.control_allow_discharge else 1 + ) if self.trigger_force_disable_discharge is not None: discharge_disabled = 1 logger.info( @@ -408,20 +410,22 @@ def write_balancer(self): config = unpack_from(">H", func_config)[0] balancer_enabled = config & FUNC_BALANCE_EN # Balance is enabled, force disable OR balancer is disabled and disable force disable - if (balancer_enabled and disable_balancer) or (not balancer_enabled and not disable_balancer): + if (balancer_enabled and disable_balancer) or ( + not balancer_enabled and not disable_balancer + ): new_func_config = config ^ FUNC_BALANCE_EN if new_func_config: with self.eeprom(writable=True): - reply = self.read_serial_data_llt(writeCmd(REG_FUNC_CONFIG, new_func_config)) + reply = self.read_serial_data_llt( + writeCmd(REG_FUNC_CONFIG, new_func_config) + ) if reply is False: logger.error("write force disable balancer failed") return False return True - - def refresh_data(self): result = self.read_gen_data() result = result and self.read_cell_data() From bb93ee3269c2bb8e49a5d97f0cf67ec7b52dd482 Mon Sep 17 00:00:00 2001 From: Paul Strawder Date: Tue, 23 May 2023 16:40:10 +0200 Subject: [PATCH 203/209] JDB BLE support (#499) * Implementing JBD BLE support. It is built upon Bleak * Additionally, it provides some handling of for up to 4 temperature probes labels T1 (NTC2), T2 (NTC3), T3 (NTC4) and T4 (NTC5). NTC1 is the BMS module temperature itself * The device page has been extend to provide more details about the actual used hardware (= product name), firmware version and BLE address --- etc/dbus-serialbattery/battery.py | 104 ++++++-- etc/dbus-serialbattery/bms/lltjbd.py | 294 ++++++++++++++++++++-- etc/dbus-serialbattery/bms/lltjbd_ble.py | 44 ++-- etc/dbus-serialbattery/bms/mnb.py | 4 +- etc/dbus-serialbattery/config.default.ini | 12 +- etc/dbus-serialbattery/dbushelper.py | 16 +- etc/dbus-serialbattery/utils.py | 6 + requirements.txt | 1 + 8 files changed, 408 insertions(+), 73 deletions(-) diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py index 2b8c8d9d..fba9cd35 100644 --- a/etc/dbus-serialbattery/battery.py +++ b/etc/dbus-serialbattery/battery.py @@ -96,6 +96,8 @@ def init_values(self): self.temp_sensors = None self.temp1 = None self.temp2 = None + self.temp3 = None + self.temp4 = None self.temp_mos = None self.cells: List[Cell] = [] self.control_charging = None @@ -127,6 +129,15 @@ def test_connection(self) -> bool: # return false when failed, true if successful return False + def connection_name(self) -> str: + return "Serial " + self.port + + def custom_name(self) -> str: + return "SerialBattery(" + self.type + ")" + + def product_name(self) -> str: + return "SerialBattery(" + self.type + ")" + @abstractmethod def get_settings(self) -> bool: """ @@ -164,6 +175,10 @@ def to_temp(self, sensor: int, value: float) -> None: self.temp1 = min(max(value, -20), 100) if sensor == 2: self.temp2 = min(max(value, -20), 100) + if sensor == 3: + self.temp3 = min(max(value, -20), 100) + if sensor == 4: + self.temp4 = min(max(value, -20), 100) def manage_charge_voltage(self) -> None: """ @@ -808,14 +823,10 @@ def get_balancing(self) -> int: return 1 return 0 - def extract_from_temp_values(self, extractor) -> Union[float, None]: - if self.temp1 is not None and self.temp2 is not None: - return extractor(self.temp1, self.temp2) - if self.temp1 is not None and self.temp2 is None: - return self.temp1 - if self.temp1 is None and self.temp2 is not None: - return self.temp2 - else: + def get_temperatures(self) -> Union[List[float], None]: + temperatures = [self.temp1, self.temp2, self.temp3, self.temp4] + result = [(t, i) for (t, i) in enumerate(temperatures) if t is not None] + if not result: return None def get_temp(self) -> Union[float, None]: @@ -824,46 +835,93 @@ def get_temp(self) -> Union[float, None]: return self.temp1 elif utils.TEMP_BATTERY == 2: return self.temp2 + elif utils.TEMP_BATTERY == 3: + return self.temp3 + elif utils.TEMP_BATTERY == 4: + return self.temp4 else: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: round( - (float(temp1) + float(temp2)) / 2, 2 - ) - ) + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] + n = len(temps) + if not temps or n == 0: + return None + data = sorted(temps) + if n % 2 == 1: + return data[n // 2] + else: + i = n // 2 + return (data[i - 1] + data[i]) / 2 except TypeError: return None def get_min_temp(self) -> Union[float, None]: try: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: min(temp1, temp2) - ) + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] + if not temps: + return None + return min(temps) except TypeError: return None def get_min_temp_id(self) -> Union[str, None]: try: - if self.temp1 < self.temp2: + temps = [ + (t, i) + for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) + if t is not None + ] + if not temps: + return None + index = min(temps)[1] + if index == 0: return utils.TEMP_1_NAME - else: + if index == 1: return utils.TEMP_2_NAME + if index == 2: + return utils.TEMP_3_NAME + if index == 3: + return utils.TEMP_4_NAME except TypeError: return None def get_max_temp(self) -> Union[float, None]: try: - return self.extract_from_temp_values( - extractor=lambda temp1, temp2: max(temp1, temp2) - ) + temps = [ + t + for t in [self.temp1, self.temp2, self.temp3, self.temp4] + if t is not None + ] + if not temps: + return None + return max(temps) except TypeError: return None def get_max_temp_id(self) -> Union[str, None]: try: - if self.temp1 > self.temp2: + temps = [ + (t, i) + for i, t in enumerate([self.temp1, self.temp2, self.temp3, self.temp4]) + if t is not None + ] + if not temps: + return None + index = max(temps)[1] + if index == 0: return utils.TEMP_1_NAME - else: + if index == 1: return utils.TEMP_2_NAME + if index == 2: + return utils.TEMP_3_NAME + if index == 3: + return utils.TEMP_4_NAME except TypeError: return None diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index c1bf2ae3..4c091aff 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -5,6 +5,166 @@ from struct import unpack_from import struct +# Protocol registers +REG_ENTER_FACTORY = 0x00 +REG_EXIT_FACTORY = 0x01 +# REG_UNKNOWN = 0x02 +REG_GENERAL = 0x03 +REG_CELL = 0x04 +REG_HARDWARE = 0x05 +# Firmware 0x16+ +REG_USE_PASSWORD = 0x06 +REG_SET_PASSWORD = 0x07 +# REG_UNKNOWN2 = 0x08 - Maybe define master password? +REG_CLEAR_PASSWORD = 0x09 + +REG_FRESET = 0x0A + +REG_DESIGN_CAP = 0x10 +REG_CYCLE_CAP = 0x11 +REG_CAP_100 = 0x12 +REG_CAP_0 = 0x13 +REG_SELF_DSG_RATE = 0x14 +REG_MFG_DATE = 0x15 +REG_SERIAL_NUM = 0x16 +REG_CYCLE_CNT = 0x17 +REG_CHGOT = 0x18 +REG_CHGOT_REL = 0x19 +REG_CHGUT = 0x1A +REG_CHGUT_REL = 0x1B +REG_DSGOT = 0x1C +REG_DSGOT_REL = 0x1D +REG_DSGUT = 0x1E +REG_DSGUT_REL = 0x1F +REG_POVP = 0x20 +REG_POVP_REL = 0x21 +REG_PUVP = 0x22 +REG_PUVP_REL = 0x23 +REG_COVP = 0x24 +REG_COVP_REL = 0x25 +REG_CUVP = 0x26 +REG_CUVP_REL = 0x27 +REG_CHGOC = 0x28 +REG_DSGOC = 0x29 +REG_BAL_START = 0x2A +REG_BAL_WINDOW = 0x2B +REG_SHUNT_RES = 0x2C +REG_FUNC_CONFIG = 0x2D +REG_NTC_CONFIG = 0x2E +REG_CELL_CNT = 0x2F +REG_FET_TIME = 0x30 +REG_LED_TIME = 0x31 +REG_CAP_80 = 0x32 +REG_CAP_60 = 0x33 +REG_CAP_40 = 0x34 +REG_CAP_20 = 0x35 +REG_COVP_HIGH = 0x36 +REG_CUVP_HIGH = 0x37 +REG_SC_DSGOC2 = 0x38 +REG_CXVP_HIGH_DELAY_SC_REL = 0x39 +REG_CHG_T_DELAYS = 0x3A +REG_DSG_T_DELAYS = 0x3B +REG_PACK_V_DELAYS = 0x3C +REG_CELL_V_DELAYS = 0x3D +REG_CHGOC_DELAYS = 0x3E +REG_DSGOC_DELAYS = 0x3F +REG_GPSOFF = 0x40 +REG_GPSOFF_TIME = 0x41 +REG_CAP_90 = 0x42 +REG_CAP_70 = 0x43 +REG_CAP_50 = 0x44 +REG_CAP_30 = 0x45 +REG_CAP_10 = 0x46 +# REG_CAP2_100 = 0x47 + +# [0x48, 0x9F] - 87 registers + +REG_MFGNAME = 0xA0 +REG_MODEL = 0xA1 +REG_BARCODE = 0xA2 +REG_ERROR = 0xAA +# 0xAB +# 0xAC +REG_CAL_CUR_IDLE = 0xAD +REG_CAL_CUR_CHG = 0xAE +REG_CAL_CUR_DSG = 0xAF + +REG_CAL_V_CELL_01 = 0xB0 +REG_CAL_V_CELL_02 = 0xB1 +REG_CAL_V_CELL_03 = 0xB2 +REG_CAL_V_CELL_04 = 0xB3 +REG_CAL_V_CELL_05 = 0xB4 +REG_CAL_V_CELL_06 = 0xB5 +REG_CAL_V_CELL_07 = 0xB6 +REG_CAL_V_CELL_08 = 0xB7 +REG_CAL_V_CELL_09 = 0xB8 +REG_CAL_V_CELL_10 = 0xB9 +REG_CAL_V_CELL_11 = 0xBA +REG_CAL_V_CELL_12 = 0xBB +REG_CAL_V_CELL_13 = 0xBC +REG_CAL_V_CELL_14 = 0xBD +REG_CAL_V_CELL_15 = 0xBE +REG_CAL_V_CELL_16 = 0xBF +REG_CAL_V_CELL_17 = 0xC0 +REG_CAL_V_CELL_18 = 0xC1 +REG_CAL_V_CELL_19 = 0xC2 +REG_CAL_V_CELL_20 = 0xC3 +REG_CAL_V_CELL_21 = 0xC4 +REG_CAL_V_CELL_22 = 0xC5 +REG_CAL_V_CELL_23 = 0xC6 +REG_CAL_V_CELL_24 = 0xC7 +REG_CAL_V_CELL_25 = 0xC8 +REG_CAL_V_CELL_26 = 0xC9 +REG_CAL_V_CELL_27 = 0xCA +REG_CAL_V_CELL_28 = 0xCB +REG_CAL_V_CELL_29 = 0xCC +REG_CAL_V_CELL_30 = 0xCD +REG_CAL_V_CELL_31 = 0xCE +REG_CAL_V_CELL_32 = 0xCF + +REG_CAL_T_NTC_0 = 0xD0 +REG_CAL_T_NTC_1 = 0xD1 +REG_CAL_T_NTC_2 = 0xD2 +REG_CAL_T_NTC_3 = 0xD3 +REG_CAL_T_NTC_4 = 0xD4 +REG_CAL_T_NTC_5 = 0xD5 +REG_CAL_T_NTC_6 = 0xD6 +REG_CAL_T_NTC_7 = 0xD7 + +REG_CAP_REMAINING = 0xE0 +REG_CTRL_MOSFET = 0xE1 +REG_CTRL_BALANCE = 0xE2 +REG_RESET = 0xE3 + +# Protocol commands +CMD_ENTER_FACTORY_MODE = b"\x56\x78" +CMD_EXIT_FACTORY_MODE = b"\x00\x00" +CMD_EXIT_AND_SAVE_FACTORY_MODE = b"\x28\x28" + + +def checksum(payload): + return (0x10000 - sum(payload)) % 0x10000 + + +def cmd(op, reg, data): + payload = [reg, len(data)] + list(data) + chksum = checksum(payload) + data = [0xDD, op] + payload + [chksum, 0x77] + format = f">BB{len(payload)}BHB" + return struct.pack(format, *data) + + +def readCmd(reg, data=None): + if data is None: + data = [] + return cmd(0xA5, reg, data) + + +def writeCmd(reg, data=None): + if data is None: + data = [] + return cmd(0x5A, reg, data) + class LltJbdProtection(Protection): def __init__(self): @@ -51,37 +211,84 @@ def __init__(self, port, baud, address): super(LltJbd, self).__init__(port, baud, address) self.protection = LltJbdProtection() self.type = self.BATTERYTYPE + self._product_name: str = "" + self.has_settings = 0 + self.reset_soc = 100 + self.soc_to_set = None + self.factory_mode = False + self.writable = False # degree_sign = u'\N{DEGREE SIGN}' - command_general = b"\xDD\xA5\x03\x00\xFF\xFD\x77" - command_cell = b"\xDD\xA5\x04\x00\xFF\xFC\x77" - command_hardware = b"\xDD\xA5\x05\x00\xFF\xFB\x77" BATTERYTYPE = "LLT/JBD" LENGTH_CHECK = 6 LENGTH_POS = 3 + command_general = readCmd(REG_GENERAL) # b"\xDD\xA5\x03\x00\xFF\xFD\x77" + command_cell = readCmd(REG_CELL) # b"\xDD\xA5\x04\x00\xFF\xFC\x77" + command_hardware = readCmd(REG_HARDWARE) # b"\xDD\xA5\x05\x00\xFF\xFB\x77" + def test_connection(self): # call a function that will connect to the battery, send a command and retrieve the result. # The result or call should be unique to this BMS. Battery name or version, etc. # Return True if success, False for failure result = False try: - result = self.read_hardware_data() + result = self.get_settings() + if result: + result = result and self.read_hardware_data() # get first data to show in startup log if result: - self.refresh_data() + result = result and self.refresh_data() except Exception as err: logger.error(f"Unexpected {err=}, {type(err)=}") result = False return result + def product_name(self) -> str: + return self._product_name + def get_settings(self): - self.read_gen_data() + if not self.read_gen_data(): + return False self.max_battery_charge_current = utils.MAX_BATTERY_CHARGE_CURRENT self.max_battery_discharge_current = utils.MAX_BATTERY_DISCHARGE_CURRENT + with self.eeprom(writable=False): + charge_over_current = self.read_serial_data_llt(readCmd(REG_CHGOC)) + if charge_over_current: + self.max_battery_charge_current = float( + unpack_from(">h", charge_over_current)[0] / 100.0 + ) + discharge_over_current = self.read_serial_data_llt(readCmd(REG_DSGOC)) + if discharge_over_current: + self.max_battery_discharge_current = float( + unpack_from(">h", discharge_over_current)[0] / -100.0 + ) + return True + def reset_soc_callback(self, path, value): + if value is None: + return False + + if value < 0 or value > 100: + return False + + self.reset_soc = value + self.soc_to_set = value + return True + + def write_soc(self): + if self.soc_to_set is None or self.soc_to_set != 100 or not self.voltage: + return False + logger.info(f"write soc {self.soc_to_set}%") + self.soc_to_set = None # Reset value, so we will set it only once + # TODO implement logic to map current pack readings into + # REG_CAP_100, REG_CAP_90, REG_CAP_80, REG_CAP_70, REG_CAP_60, ... + with self.eeprom(writable=True): + pack_voltage = struct.pack(">H", int(self.voltage * 10)) + self.read_serial_data_llt(writeCmd(REG_CAP_100, pack_voltage)) + def refresh_data(self): result = self.read_gen_data() result = result and self.read_cell_data() @@ -200,7 +407,9 @@ def read_gen_data(self): self.capacity_remain = capacity_remain / 100 self.capacity = capacity / 100 self.to_cell_bits(balance, balance2) - self.version = float(str(version >> 4 & 0x0F) + "." + str(version & 0x0F)) + self.hardware_version = float( + str(version >> 4 & 0x0F) + "." + str(version & 0x0F) + ) self.to_fet_bits(fet) self.to_protection_bits(protection) self.max_battery_voltage = utils.MAX_CELL_VOLTAGE * self.cell_count @@ -234,24 +443,73 @@ def read_hardware_data(self): if hardware_data is False: return False - self.hardware_version = unpack_from( + self._product_name = unpack_from( ">" + str(len(hardware_data)) + "s", hardware_data )[0].decode() - logger.debug(self.hardware_version) + logger.debug(self._product_name) return True + @staticmethod + def validate_packet(data): + if not data: + return False + + if data is False: + return False + + start, op, status, payload_length = unpack_from("BBBB", data) + if start != 0xDD: + logger.error( + ">>> ERROR: Invalid response packet. Expected begin packet character 0xDD" + ) + if status != 0x0: + logger.warn(">>> WARN: BMS rejected request. Status " + status) + return False + if len(data) != payload_length + 7: + logger.error( + ">>> ERROR: BMS send insufficient data. Received " + + str(len(data)) + + " expected " + + str(payload_length + 7) + ) + return False + chk_sum, end = unpack_from(">HB", data, payload_length + 4) + if end != 0x77: + logger.error( + ">>> ERROR: Incorrect Reply. Expected end packet character 0x77" + ) + return False + if chk_sum != checksum(data[2:-3]): + logger.error(">>> ERROR: Invalid checksum.") + return False + + payload = data[4 : payload_length + 4] + + return payload + def read_serial_data_llt(self, command): data = read_serial_data( command, self.port, self.baud_rate, self.LENGTH_POS, self.LENGTH_CHECK ) - if data is False: - return False + return self.validate_packet(data) - start, flag, command_ret, length = unpack_from("BBBB", data) - checksum, end = unpack_from("HB", data, length + 4) + def __enter__(self): + if self.read_serial_data_llt( + writeCmd(REG_ENTER_FACTORY, CMD_ENTER_FACTORY_MODE) + ): + self.factory_mode = True - if end == 119: - return data[4 : length + 4] - else: - logger.error(">>> ERROR: Incorrect Reply") - return False + def __exit__(self, type, value, traceback): + cmd_value = ( + CMD_EXIT_AND_SAVE_FACTORY_MODE if self.writable else CMD_EXIT_FACTORY_MODE + ) + if self.factory_mode: + if not self.read_serial_data_llt(writeCmd(REG_EXIT_FACTORY, cmd_value)): + logger.error(">>> ERROR: Unable to exit factory mode.") + else: + self.factory_mode = False + self.writable = False + + def eeprom(self, writable=False): + self.writable = writable + return self diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index fa4b38da..de995492 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -3,13 +3,12 @@ import atexit import functools import threading +from asyncio import CancelledError from typing import Union, Optional from utils import logger -from struct import unpack_from from bleak import BleakClient, BleakScanner, BLEDevice from bms.lltjbd import LltJbdProtection, LltJbd - BLE_SERVICE_UUID = "0000ff00-0000-1000-8000-00805f9b34fb" BLE_CHARACTERISTICS_TX_UUID = "0000ff02-0000-1000-8000-00805f9b34fb" BLE_CHARACTERISTICS_RX_UUID = "0000ff01-0000-1000-8000-00805f9b34fb" @@ -21,7 +20,9 @@ class LltJbd_Ble(LltJbd): BATTERYTYPE = "LltJbd_Ble" def __init__(self, port: Optional[str], baud: Optional[int], address: str): - super(LltJbd_Ble, self).__init__(address.replace(":", "").lower(), -1, address) + super(LltJbd_Ble, self).__init__( + "ble" + address.replace(":", "").lower(), -1, address + ) self.address = address self.protection = LltJbdProtection() @@ -50,9 +51,14 @@ def on_disconnect(self, client): logger.info("BLE client disconnected") async def bt_main_loop(self): - self.device = await BleakScanner.find_device_by_address( - self.address, cb=dict(use_bdaddr=True) - ) + try: + self.device = await BleakScanner.find_device_by_address( + self.address, cb=dict(use_bdaddr=True) + ) + except Exception as e: + logger.error(">>> ERROR: Bluetooth stack failed.", e) + self.device = None + await asyncio.sleep(0.5) if not self.device: self.run = False @@ -155,22 +161,18 @@ async def async_read_serial_data_llt(self, command): def read_serial_data_llt(self, command): if not self.bt_loop: return False - data = asyncio.run(self.async_read_serial_data_llt(command)) - if not data: + try: + data = asyncio.run(self.async_read_serial_data_llt(command)) + return self.validate_packet(data) + except CancelledError as e: + logger.error(">>> ERROR: No reply - canceled - returning", e) return False - - start, flag, command_ret, length = unpack_from("BBBB", data) - checksum, end = unpack_from("HB", data, length + 4) - - if end == 119: - return data[4 : length + 4] - else: - logger.error(">>> ERROR: Incorrect Reply") + except Exception as e: + logger.error(">>> ERROR: No reply - returning", e) return False -""" -async def test_LltJbd_Ble(): +if __name__ == "__main__": import sys bat = LltJbd_Ble("Foo", -1, sys.argv[1]) @@ -178,8 +180,4 @@ async def test_LltJbd_Ble(): logger.error(">>> ERROR: Unable to connect") else: bat.refresh_data() - - -if __name__ == "__main__": - test_LltJbd_Ble() -""" + bat.get_settings() diff --git a/etc/dbus-serialbattery/bms/mnb.py b/etc/dbus-serialbattery/bms/mnb.py index 1ad1126e..84365866 100644 --- a/etc/dbus-serialbattery/bms/mnb.py +++ b/etc/dbus-serialbattery/bms/mnb.py @@ -169,9 +169,9 @@ def manage_charge_current(self): # Change depending on the cell_min_voltage values if self.cell_min_voltage < self.V_C_min + 0.05: - self.control_allow_dicharge = False + self.control_allow_discharge = False else: - self.control_allow_dicharge = True + self.control_allow_discharge = True if self.cell_min_voltage < self.V_C_min + 0.15: b = 10 * (self.cell_min_voltage - self.V_C_min - 0.05) diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index 4b11e9b5..e9b4f403 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -199,10 +199,12 @@ MIDPOINT_ENABLE = False ; Battery temperature -; Specifiy how the battery temperature is assembled -; 0 Get mean of temperature sensor 1 and temperature sensor 2 +; Specify how the battery temperature is assembled +; 0 Get mean of temperature sensor 1 to sensor 4 ; 1 Get only temperature from temperature sensor 1 ; 2 Get only temperature from temperature sensor 2 +; 3 Get only temperature from temperature sensor 3 +; 4 Get only temperature from temperature sensor 4 TEMP_BATTERY = 0 ; Temperature sensor 1 name @@ -211,6 +213,12 @@ TEMP_1_NAME = Temp 1 ; Temperature sensor 2 name TEMP_2_NAME = Temp 2 +; Temperature sensor 2 name +TEMP_3_NAME = Temp 3 + +; Temperature sensor 2 name +TEMP_4_NAME = Temp 4 + ; --------- BMS specific settings --------- diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py index ccf7181e..0cca3187 100644 --- a/etc/dbus-serialbattery/dbushelper.py +++ b/etc/dbus-serialbattery/dbushelper.py @@ -113,19 +113,17 @@ def setup_vedbus(self): self._dbusservice.add_path( "/Mgmt/ProcessVersion", "Python " + platform.python_version() ) - self._dbusservice.add_path("/Mgmt/Connection", "Serial " + self.battery.port) + self._dbusservice.add_path("/Mgmt/Connection", self.battery.connection_name()) # Create the mandatory objects self._dbusservice.add_path("/DeviceInstance", self.instance) self._dbusservice.add_path("/ProductId", 0x0) - self._dbusservice.add_path( - "/ProductName", "SerialBattery(" + self.battery.type + ")" - ) + self._dbusservice.add_path("/ProductName", self.battery.product_name()) self._dbusservice.add_path("/FirmwareVersion", str(utils.DRIVER_VERSION)) self._dbusservice.add_path("/HardwareVersion", self.battery.hardware_version) self._dbusservice.add_path("/Connected", 1) self._dbusservice.add_path( - "/CustomName", "SerialBattery(" + self.battery.type + ")", writeable=True + "/CustomName", self.battery.custom_name(), writeable=True ) self._dbusservice.add_path( "/Serial", self.battery.unique_identifier, writeable=True @@ -231,6 +229,10 @@ def setup_vedbus(self): self._dbusservice.add_path("/System/MaxCellTemperature", None, writeable=True) self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True) self._dbusservice.add_path("/System/MOSTemperature", None, writeable=True) + self._dbusservice.add_path("/System/Temperature1", None, writeable=True) + self._dbusservice.add_path("/System/Temperature2", None, writeable=True) + self._dbusservice.add_path("/System/Temperature3", None, writeable=True) + self._dbusservice.add_path("/System/Temperature4", None, writeable=True) self._dbusservice.add_path( "/System/MaxCellVoltage", None, @@ -470,6 +472,10 @@ def publish_dbus(self): "/System/MaxTemperatureCellId" ] = self.battery.get_max_temp_id() self._dbusservice["/System/MOSTemperature"] = self.battery.get_mos_temp() + self._dbusservice["/System/Temperature1"] = self.battery.temp1 + self._dbusservice["/System/Temperature2"] = self.battery.temp2 + self._dbusservice["/System/Temperature3"] = self.battery.temp3 + self._dbusservice["/System/Temperature4"] = self.battery.temp4 # Voltage control self._dbusservice["/Info/MaxChargeVoltage"] = self.battery.control_voltage diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index e36782da..8b188446 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -294,6 +294,12 @@ def _get_list_from_config( # Temperature sensor 2 name TEMP_2_NAME = config["DEFAULT"]["TEMP_2_NAME"] +# Temperature sensor 3 name +TEMP_3_NAME = config["DEFAULT"]["TEMP_3_NAME"] + +# Temperature sensor 2 name +TEMP_4_NAME = config["DEFAULT"]["TEMP_4_NAME"] + # --------- BMS specific settings --------- diff --git a/requirements.txt b/requirements.txt index 8bec2184..6de24eea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pyserial==3.5 minimalmodbus==2.0.1 +bleak==0.20.0 \ No newline at end of file From a0a5ebfb5907d7caf2a49d8c2be768e09a386f73 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 23 May 2023 17:02:58 +0200 Subject: [PATCH 204/209] updated changelog --- CHANGELOG.md | 1 + etc/dbus-serialbattery/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68bf2917..c4f88750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ * Added: JKBMS BLE - Show if balancing is active and which cells are balancing by @mr-manuel * Added: JKBMS BLE - Show serial number and "User Private Data" field that can be set in the JKBMS App to identify the BMS in a multi battery environment by @mr-manuel * Added: JKBMS BLE driver by @baranator +* Added: LLT/JBD BMS BLE driver by @idstein * Added: Possibility to add `config.ini` to the root of a USB flash drive on install via the USB method by @mr-manuel * Added: Possibility to configure a `VOLTAGE_DROP` voltage, if you are using a SmartShunt as battery monitor as there is a little voltage difference https://github.com/Louisvdw/dbus-serialbattery/discussions/632 by @mr-manuel * Added: Post install notes by @mr-manuel diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index b98197e0..c854c4f3 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -35,7 +35,7 @@ def _get_list_from_config( # if not specified: baud = 9600 # Constants - Need to dynamically get them in future -DRIVER_VERSION = "1.0.20230521dev" +DRIVER_VERSION = "1.0.20230523dev" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From f19e004ed643180aa909dbf4a6fb2208bf6cfbcb Mon Sep 17 00:00:00 2001 From: Raphael Mack Date: Wed, 24 May 2023 16:59:13 +0200 Subject: [PATCH 205/209] Add Supoprt for HeltecSmartBMS (YYBMS) using modbus via RS485 connection (#658) --- docs/docs/general/features.md | 44 +- docs/docs/general/install.md | 2 +- docs/docs/general/supported-bms.md | 3 + etc/dbus-serialbattery/bms/heltecmodbus.py | 439 +++++++++++++++++++ etc/dbus-serialbattery/config.default.ini | 6 + etc/dbus-serialbattery/dbus-serialbattery.py | 2 + etc/dbus-serialbattery/utils.py | 5 + 7 files changed, 478 insertions(+), 23 deletions(-) create mode 100644 etc/dbus-serialbattery/bms/heltecmodbus.py diff --git a/docs/docs/general/features.md b/docs/docs/general/features.md index a7b2434c..2ed2e9b3 100644 --- a/docs/docs/general/features.md +++ b/docs/docs/general/features.md @@ -83,7 +83,7 @@ CCCM limits the charge/discharge current depending on the highest/lowest cell vo * between `2.8V - 2.9V` → `5A `discharge * below `<= 2.70V` → `0A` discharge -### Temprature +### Temperature * `CCCM_T_ENABLE = True/False` * `DCCM_T_ENABLE = True/False` @@ -121,27 +121,27 @@ If the `MAX_CELL_VOLTAGE` \* `cell count` is reached for `MAX_VOLTAGE_TIME_SEC` ## BMS feature comparison -| Feature | Ant | Daly | ECS | HLPdataBMS4S | JK BMS | Life/Tian Power | LLT/JBD | MNB (1) | Renogy | Seplos | Sinowealth (1) | -| ---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | -| Voltage | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Current | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Power | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| State Of Charge | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Battery temperature | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| MOSFET temperature | No | No | No | No | Yes | No | Yes | No | No | No | No | -| Consumed Ah | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Time-to-go | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | -| Min/max cell voltages | Yes | Yes | No | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | -| Min/max temperature | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Installed capacity | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Available capacity | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Cell details | No | Yes | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | ? | -| Balancing status | Yes | No | Yes | No | Yes | Yes | No | No | No | No | ? | -| Raise alarms from the BMS | Yes | Yes | Yes (2) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | -| History of charge cycles | Yes | Yes | No | No | Yes | Yes | Yes | No | Yes | Yes | Yes | -| Get CCL/DCL from the BMS | No | No | No | No | Yes | No | No | No | No | No | No | -| Charge current control management (CCCM) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| Set battery parameters (DVCC) | Calc | Calc | Yes | Yes | Calc | Calc | Calc | Yes | Calc | Calc | Calc | +| Feature | Ant | Daly | ECS | Heltec | HLPdataBMS4S | JK BMS | Life/Tian Power | LLT/JBD | MNB (1) | Renogy | Seplos | Sinowealth (1) | +| ---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| Voltage | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Current | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Power | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| State Of Charge | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Battery temperature | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| MOSFET temperature | No | No | No | Yes | No | Yes | No | Yes | No | No | No | No | +| Consumed Ah | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Time-to-go | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | Calc | +| Min/max cell voltages | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | +| Min/max temperature | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Installed capacity | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Available capacity | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Cell details | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | ? | +| Balancing status | Yes | No | Yes | Yes | No | Yes | Yes | No | No | No | No | ? | +| Raise alarms from the BMS | Yes | Yes | Yes (2) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | +| History of charge cycles | Yes | Yes | No | No | No | Yes | Yes | Yes | No | Yes | Yes | Yes | +| Get CCL/DCL from the BMS | No | No | No | Yes | No | Yes | No | No | No | No | No | No | +| Charge current control management (CCCM) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Set battery parameters (DVCC) | Calc | Calc | Yes | Calc | Yes | Calc | Calc | Calc | Yes | Calc | Calc | Calc | `Calc` means that the value is calculated by the driver. diff --git a/docs/docs/general/install.md b/docs/docs/general/install.md index 56c891bc..4b30e030 100644 --- a/docs/docs/general/install.md +++ b/docs/docs/general/install.md @@ -122,7 +122,7 @@ Select `2` for `nightly build` and then select the branch you want to install fr ### BMS specific settings * ECS BMS → https://github.com/Louisvdw/dbus-serialbattery/issues/254#issuecomment-1275924313 - +* HeltecModbus → in case the modbus slave address of the BMS was adjusted from the factory default, configure the slave addresses to query in config.ini:HELTEC_MODBUS_ADDR. As always the battery settings shall be configured in the BMS already via app or computer. ## How to change the default limits diff --git a/docs/docs/general/supported-bms.md b/docs/docs/general/supported-bms.md index 416e7362..ff247950 100644 --- a/docs/docs/general/supported-bms.md +++ b/docs/docs/general/supported-bms.md @@ -22,6 +22,9 @@ Disabled by default since driver version `v0.14.0` as it causes other issues. Se ### • ECS GreenMeter with LiPro +### • HeltecModbus SmartBMS (YanYang BMS) +Communication to the Heltec SmartBMS (which is a rebranded YYBMS) via Modbus/RS485. + ### • HLPdataBMS4S ### • [JKBMS](https://www.jkbms.com/products/) / Heltec BMS diff --git a/etc/dbus-serialbattery/bms/heltecmodbus.py b/etc/dbus-serialbattery/bms/heltecmodbus.py new file mode 100644 index 00000000..9291fd26 --- /dev/null +++ b/etc/dbus-serialbattery/bms/heltecmodbus.py @@ -0,0 +1,439 @@ +# -*- coding: utf-8 -*- +# known limitations: +# - only BMS variants with 2 cell temperature sensors supported +# - some "interesting" datapoints are not read (e. g. registers 52: switch type, 62: bootloader and firmware version) +# - SOC not yet resettable from Venus (similary to Daly for support of writing SOC), but modbus write to 120 should be +# fairly possible) + + +from battery import Battery, Cell +from utils import logger +import utils +import serial +import time +import minimalmodbus +from typing import Dict +import threading + +# the Heltec BMS is not always as responsive as it should, so let's try it up to (RETRYCNT - 1) times to talk to it +RETRYCNT = 10 + +# the wait time after a communication - normally this should be as defined by modbus RTU and handled in minimalmodbus, +# but yeah, it seems we need it for the Heltec BMS +SLPTIME = 0.03 + +mbdevs: Dict[int, minimalmodbus.Instrument] = {} +locks: Dict[int, any] = {} + + +class HeltecModbus(Battery): + def __init__(self, port, baud, address): + super(HeltecModbus, self).__init__(port, baud, address) + self.type = "Heltec_Smart" + + def test_connection(self): + # call a function that will connect to the battery, send a command and retrieve the result. + # The result or call should be unique to this BMS. Battery name or version, etc. + # Return True if success, False for failure + for self.address in utils.HELTEC_MODBUS_ADDR: + logger.info("Testing on slave address " + str(self.address)) + found = False + if self.address not in locks: + locks[self.address] = threading.Lock() + + # TODO: We need to lock not only based on the address, but based on the port as soon as multiple BMSs + # are supported on the same serial interface. Then locking on the port will be enough. + + with locks[self.address]: + mbdev = minimalmodbus.Instrument( + self.port, + slaveaddress=self.address, + mode="rtu", + close_port_after_each_call=True, + debug=False, + ) + mbdev.serial.parity = minimalmodbus.serial.PARITY_NONE + mbdev.serial.stopbits = serial.STOPBITS_ONE + mbdev.serial.baudrate = 9600 + # yes, 400ms is long but the BMS is sometimes really slow in responding, so this is a good compromise + mbdev.serial.timeout = 0.4 + mbdevs[self.address] = mbdev + + for n in range(1, RETRYCNT): + try: + string = mbdev.read_string(7, 13) + time.sleep(SLPTIME) + found = True + logger.info( + "found in try " + + str(n) + + "/" + + str(RETRYCNT) + + " for " + + self.port + + "(" + + str(self.address) + + "): " + + string + ) + except Exception as e: + logger.warn( + "testing failed (" + + str(e) + + ") " + + str(n) + + "/" + + str(RETRYCNT) + + " for " + + self.port + + "(" + + str(self.address) + + ")" + ) + continue + break + if found: + self.type = "#" + str(self.address) + "_Heltec_Smart" + break + + return ( + found + and self.read_status_data() + and self.get_settings() + and self.refresh_data() + ) + + def get_settings(self): + self.max_battery_voltage = self.max_cell_voltage * self.cell_count + self.min_battery_voltage = self.min_cell_voltage * self.cell_count + + return True + + def refresh_data(self): + # call all functions that will refresh the battery data. + # This will be called for every iteration (1 second) + # Return True if success, False for failure + return self.read_soc_data() and self.read_cell_data() + + def read_status_data(self): + mbdev = mbdevs[self.address] + + with locks[self.address]: + for n in range(1, RETRYCNT): + try: + ccur = mbdev.read_register(191, 0, 3, False) + self.max_battery_charge_current = ( + (int)(((ccur & 0xFF) << 8) | ((ccur >> 8) & 0xFF)) + ) / 100 + time.sleep(SLPTIME) + + dc = mbdev.read_register(194, 0, 3, False) + self.max_battery_discharge_current = ( + ((dc & 0xFF) << 8) | ((dc >> 8) & 0xFF) + ) / 100 + time.sleep(SLPTIME) + + cap = mbdev.read_register(118, 0, 3, False) + self.capacity = (((cap & 0xFF) << 8) | ((cap >> 8) & 0xFF)) / 10 + time.sleep(SLPTIME) + + cap = mbdev.read_register(119, 0, 3, False) + self.actual_capacity = ( + ((cap & 0xFF) << 8) | ((cap >> 8) & 0xFF) + ) / 10 + time.sleep(SLPTIME) + + cap = mbdev.read_register(126, 0, 3, False) + self.learned_capacity = ( + ((cap & 0xFF) << 8) | ((cap >> 8) & 0xFF) + ) / 10 + time.sleep(SLPTIME) + + volt = mbdev.read_register(169, 0, 3, False) + self.max_cell_voltage = ( + ((volt & 0xFF) << 8) | ((volt >> 8) & 0xFF) + ) / 1000 + time.sleep(SLPTIME) + + volt = mbdev.read_register(172, 0, 3, False) + self.min_cell_voltage = ( + ((volt & 0xFF) << 8) | ((volt >> 8) & 0xFF) + ) / 1000 + time.sleep(SLPTIME) + + string = mbdev.read_string(7, 13) + self.hwTypeName = string + time.sleep(SLPTIME) + + string = mbdev.read_string(41, 6) + self.devName = string + time.sleep(SLPTIME) + + serial1 = mbdev.read_registers(2, number_of_registers=4) + self.unique_identifier = "-".join( + "{:04x}".format(x) for x in serial1 + ) + time.sleep(SLPTIME) + + self.pw = mbdev.read_string(47, 2) + time.sleep(SLPTIME) + + tmp = mbdev.read_register(75) + # h: batterytype: 0: Ternery Lithium, 1: Iron Lithium, 2: Lithium Titanat + # l: #of cells + + self.cell_count = (tmp >> 8) & 0xFF + tmp = tmp & 0xFF + if tmp == 0: + self.cellType = "Ternary Lithium" + elif tmp == 1: + self.cellType = "Iron Lithium" + elif tmp == 2: + self.cellType = "Lithium Titatnate" + else: + self.cellType = "unknown" + time.sleep(SLPTIME) + + self.hardware_version = ( + self.devName + + "(" + + str((mbdev.read_register(38) >> 8) & 0xFF) + + ")" + ) + time.sleep(SLPTIME) + + date = mbdev.read_long(39, 3, True, minimalmodbus.BYTEORDER_LITTLE) + self.production_date = ( + str(date & 0xFFFF) + + "-" + + str((date >> 24) & 0xFF) + + "-" + + str((date >> 16) & 0xFF) + ) + time.sleep(SLPTIME) + + # we finished all readings without trouble, so let's break from the retry loop + break + except Exception as e: + logger.warn( + "Error reading settings from BMS, retry (" + + str(n) + + "/" + + str(RETRYCNT) + + "): " + + str(e) + ) + continue + + logger.info(self.hardware_version) + logger.info("Heltec-" + self.hwTypeName) + logger.info(" Dev name: " + self.devName) + logger.info(" Serial: " + self.unique_identifier) + logger.info(" Made on: " + self.production_date) + logger.info(" Cell count: " + str(self.cell_count)) + logger.info(" Cell type: " + self.cellType) + logger.info(" BT password: " + self.pw) + logger.info(" rated capacity: " + str(self.capacity)) + logger.info(" actual capacity: " + str(self.actual_capacity)) + logger.info(" learned capacity: " + str(self.learned_capacity)) + + return True + + def read_soc_data(self): + mbdev = mbdevs[self.address] + + with locks[self.address]: + for n in range(1, RETRYCNT): + try: + self.voltage = ( + mbdev.read_long(76, 3, True, minimalmodbus.BYTEORDER_LITTLE) + / 1000 + ) + time.sleep(SLPTIME) + + self.current = -( + mbdev.read_long(78, 3, True, minimalmodbus.BYTEORDER_LITTLE) + / 100 + ) + time.sleep(SLPTIME) + + runState1 = mbdev.read_long( + 152, 3, True, minimalmodbus.BYTEORDER_LITTLE + ) + time.sleep(SLPTIME) + + # bit 29 is discharge protection + if (runState1 & 0x20000000) == 0: + self.discharge_fet = True + else: + self.discharge_fet = False + + # bit 28 is charge protection + if (runState1 & 0x10000000) == 0: + self.charge_fet = True + else: + self.charge_fet = False + + warnings = mbdev.read_long( + 156, 3, True, minimalmodbus.BYTEORDER_LITTLE + ) + if (warnings & (1 << 3)) or ( + warnings & (1 << 15) + ): # 15 is full protection, 3 is total overvoltage + self.voltage_high = 2 + else: + self.voltage_high = 0 + + if warnings & (1 << 0): + self.protection.voltage_cell_high = 2 + # we handle a single cell OV as total OV, as long as cell_high is not explicitly handled + self.protection.voltage_high = 1 + else: + self.protection.voltage_cell_high = 0 + + if warnings & (1 << 1): + self.protection.voltage_cell_low = 2 + else: + self.protection.voltage_cell_low = 0 + + if warnings & (1 << 4): + self.protection.voltage_low = 2 + else: + self.protection.voltage_low = 0 + + if warnings & (1 << 5): + self.protection.current_over = 2 + else: + self.protection.current_over = 0 + + if warnings & (1 << 7): + self.protection.current_under = 2 + elif warnings & (1 << 6): + self.protection.current_under = 1 + else: + self.protection.current_under = 0 + + if warnings & (1 << 8): # this is a short circuit + self.protection.current_over = 2 + + if warnings & (1 << 9): + self.protection.temp_high_charge = 2 + else: + self.protection.temp_high_charge = 0 + + if warnings & (1 << 10): + self.protection.temp_low_charge = 2 + else: + self.protection.temp_low_charge = 0 + + if warnings & (1 << 11): + self.protection.temp_high_discharge = 2 + else: + self.protection.temp_high_discharge = 0 + + if warnings & (1 << 12): + self.protection.temp_low_discharge = 2 + else: + self.protection.temp_low_discharge = 0 + + if warnings & (1 << 13): # MOS overtemp + self.protection.temp_high_internal = 2 + else: + self.protection.temp_high_internal = 0 + + if warnings & (1 << 14): # SOC low + self.protection.soc_low = 2 + else: + self.protection.soc_low = 0 + + if warnings & (0xFFFF0000): # any other fault + self.protection.internal_failure = 2 + else: + self.protection.internal_failure = 0 + + socsoh = mbdev.read_register(120, 0, 3, False) + self.soh = socsoh & 0xFF + self.soc = (socsoh >> 8) & 0xFF + time.sleep(SLPTIME) + + # we could read min and max temperature, here, but I have a BMS with only 2 sensors, + # so I couldn't test the logic and read therefore only the first two temperatures + # tminmax = mbdev.read_register(117, 0, 3, False) + # nmin = (tminmax & 0xFF) + # nmax = ((tminmax >> 8) & 0xFF) + + temps = mbdev.read_register(113, 0, 3, False) + self.temp1 = (temps & 0xFF) - 40 + self.temp2 = ((temps >> 8) & 0xFF) - 40 + time.sleep(SLPTIME) + + temps = mbdev.read_register(112, 0, 3, False) + most = (temps & 0xFF) - 40 + balt = ((temps >> 8) & 0xFF) - 40 + # balancer temperature is not handled separately in dbus-serialbattery, + # so let's display the max of both temperatures inside the BMS as mos temperature + self.temp_mos = max(most, balt) + time.sleep(SLPTIME) + + return True + + except Exception as e: + logger.warn( + "Error reading SOC, retry (" + + str(n) + + "/" + + str(RETRYCNT) + + ") " + + str(e) + ) + continue + break + logger.warn("Error reading SOC, failed") + return False + + def read_cell_data(self): + result = False + mbdev = mbdevs[self.address] + + with locks[self.address]: + for n in range(1, RETRYCNT): + try: + cells = mbdev.read_registers( + 81, number_of_registers=self.cell_count + ) + time.sleep(SLPTIME) + + balancing = mbdev.read_long( + 139, 3, signed=False, byteorder=minimalmodbus.BYTEORDER_LITTLE + ) + time.sleep(SLPTIME) + + result = True + except Exception as e: + logger.warn( + "read_cell_data() failed (" + + str(e) + + ") " + + str(n) + + "/" + + str(RETRYCNT) + ) + continue + break + if result is False: + return False + + if len(self.cells) != self.cell_count: + self.cells = [] + for idx in range(self.cell_count): + self.cells.append(Cell(False)) + + i = 0 + for cell in cells: + cellV = ((cell & 0xFF) << 8) | ((cell >> 8) & 0xFF) + self.cells[i].voltage = cellV / 1000 + self.cells[i].balance = balancing & (1 << i) != 0 + + i = i + 1 + + return True diff --git a/etc/dbus-serialbattery/config.default.ini b/etc/dbus-serialbattery/config.default.ini index e9b4f403..25762314 100644 --- a/etc/dbus-serialbattery/config.default.ini +++ b/etc/dbus-serialbattery/config.default.ini @@ -240,6 +240,12 @@ LIPRO_START_ADDRESS = 2 LIPRO_END_ADDRESS = 4 LIPRO_CELL_COUNT = 15 +; -- HeltecModbus (Heltec SmartBMS/YYBMS) settings +; Set the Modbus addresses from the adapters +; Separate each address to check by a comma like: 1, 2, 3, ... +; factory default address will be 1 +HELTEC_MODBUS_ADDR = 1 + ; --------- Battery monitor specific settings --------- ; If you are using a SmartShunt or something else as a battery monitor, the battery voltage reported diff --git a/etc/dbus-serialbattery/dbus-serialbattery.py b/etc/dbus-serialbattery/dbus-serialbattery.py index b50c938c..6530ace1 100644 --- a/etc/dbus-serialbattery/dbus-serialbattery.py +++ b/etc/dbus-serialbattery/dbus-serialbattery.py @@ -24,6 +24,7 @@ # import battery classes from bms.daly import Daly from bms.ecs import Ecs +from bms.heltecmodbus import HeltecModbus from bms.hlpdatabms4s import HLPdataBMS4S from bms.jkbms import Jkbms from bms.lifepower import Lifepower @@ -39,6 +40,7 @@ {"bms": Daly, "baud": 9600, "address": b"\x40"}, {"bms": Daly, "baud": 9600, "address": b"\x80"}, {"bms": Ecs, "baud": 19200}, + {"bms": HeltecModbus, "baud": 9600}, {"bms": HLPdataBMS4S, "baud": 9600}, {"bms": Jkbms, "baud": 115200}, {"bms": Lifepower, "baud": 9600}, diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index c854c4f3..8769d5a0 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -321,6 +321,11 @@ def _get_list_from_config( LIPRO_END_ADDRESS = int(config["DEFAULT"]["LIPRO_END_ADDRESS"]) LIPRO_CELL_COUNT = int(config["DEFAULT"]["LIPRO_CELL_COUNT"]) +# -- HeltecModbus device settings +HELTEC_MODBUS_ADDR = _get_list_from_config( + "DEFAULT", "HELTEC_MODBUS_ADDR", lambda v: int(v) +) + # --------- Battery monitor specific settings --------- # If you are using a SmartShunt or something else as a battery monitor, the battery voltage reported From fc95f6bdbd5439486300d443900ba9497bad91fe Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 24 May 2023 17:11:17 +0200 Subject: [PATCH 206/209] Changes 2023.05.24 (#667) * Check Venus OS version before installing --- CHANGELOG.md | 8 +- etc/dbus-serialbattery/reinstall-local.sh | 132 +++++++++++++++++----- 2 files changed, 107 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f88750..bf3497d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,20 +12,22 @@ * Added: Balancing switch status to the GUI -> SerialBattery -> IO by @mr-manuel * Added: Block charge/discharge when BMS communication is lost. Can be enabled trough the config file by @mr-manuel * Added: Charge Mode display by @mr-manuel +* Added: Check minimum required Venus OS version before installing by @mr-manuel * Added: Choose how battery temperature is assembled (mean temp 1 & 2, only temp 1 or only temp 2) by @mr-manuel * Added: Config file by @ppuetsch * Added: Create empty `config.ini` for easier user usage by @mr-manuel * Added: Cronjob to restart Bluetooth service every 12 hours by @mr-manuel +* Added: Daly BMS - Discharge / Charge Mosfet switching over remote console/GUI https://github.com/Louisvdw/dbus-serialbattery/issues/26 by @transistorgit * Added: Daly BMS - Read capacity https://github.com/Louisvdw/dbus-serialbattery/pull/594 by @transistorgit * Added: Daly BMS - Read production date and build unique identifier by @transistorgit * Added: Daly BMS - Set SoC by @transistorgit * Added: Daly BMS - Show "battery code" field that can be set in the Daly app by @transistorgit -* Added: Daly BMS - Discharge / Charge Mosfet switching over remote console/GUI https://github.com/Louisvdw/dbus-serialbattery/issues/26 by @transistorgit * Added: Device name field (found in the GUI -> SerialBattery -> Device), that show a custom string that can be set in some BMS, if available by @mr-manuel * Added: Driver uninstall script by @mr-manuel -* Added: Rename TAR file after USB/SD card install to not overwrite the data on every reboot https://github.com/Louisvdw/dbus-serialbattery/issues/638 by @mr-manuel * Added: Fix for Venus OS >= v3.00~14 showing unused items https://github.com/Louisvdw/dbus-serialbattery/issues/469 by @mr-manuel +* Added: HeltecSmartBMS driver by @ramack * Added: HighInternalTemperature alarm (MOSFET) for JKBMS by @mr-manuel +* Added: HLPdata BMS driver by @ peterohman * Added: Improved maintainability (flake8, black lint), introduced code checks and automate release build https://github.com/Louisvdw/dbus-serialbattery/pull/386 by @ppuetsch * Added: Install needed Bluetooth components automatically after a Venus OS upgrade by @mr-manuel * Added: JKBMS - MOS temperature https://github.com/Louisvdw/dbus-serialbattery/pull/440 by @baphomett @@ -45,8 +47,10 @@ * Added: Post install notes by @mr-manuel * Added: Read charge/discharge limits from JKBMS by @mr-manuel * Added: Recalculation interval in linear mode for CVL, CCL and DCL by @mr-manuel +* Added: Rename TAR file after USB/SD card install to not overwrite the data on every reboot https://github.com/Louisvdw/dbus-serialbattery/issues/638 by @mr-manuel * Added: Reset values to None, if battery goes offline (not reachable for 10s). Fixes https://github.com/Louisvdw/dbus-serialbattery/issues/193 https://github.com/Louisvdw/dbus-serialbattery/issues/64 by @transistorgit * Added: Script to install directly from repository by @mr-manuel +* Added: Seplos BMS driver by @wollew * Added: Serial number field (found in the GUI -> SerialBattery -> Device), that show the serial number or a unique identifier for the BMS, if available by @mr-manuel * Added: Show charge mode (absorption, bulk, ...) in Parameters page by @mr-manuel * Added: Show charge/discharge limitation reason by @mr-manuel diff --git a/etc/dbus-serialbattery/reinstall-local.sh b/etc/dbus-serialbattery/reinstall-local.sh index b082c620..bdc0030a 100755 --- a/etc/dbus-serialbattery/reinstall-local.sh +++ b/etc/dbus-serialbattery/reinstall-local.sh @@ -5,6 +5,63 @@ DRIVERNAME=dbus-serialbattery + +# check if minimum required Venus OS is installed | start +versionRequired="v2.90" + +# elaborate version string for better comparing +# https://github.com/kwindrem/SetupHelper/blob/ebaa65fcf23e2bea6797f99c1c41174143c1153c/updateFileSets#L56-L81 +function versionStringToNumber () +{ + local local p4="" ; local p5="" ; local p5="" + local major=""; local minor="" + + # first character should be 'v' so first awk parameter will be empty and is not prited into the read command + # + # version number formats: v2.40, v2.40~6, v2.40-large-7, v2.40~6-large-7 + # so we must adjust how we use paramters read from the version string + # and parsed by awk + # if no beta make sure release is greater than any beta (i.e., a beta portion of 999) + + read major minor p4 p5 p6 <<< $(echo $1 | awk -v FS='[v.~-]' '{print $2, $3, $4, $5, $6}') + ((versionNumber = major * 1000000000 + minor * 1000000)) + if [ -z $p4 ] || [ $p4 = "large" ]; then + ((versionNumber += 999)) + else + ((versionNumber += p4)) + fi + if [ ! -z $p4 ] && [ $p4 = "large" ]; then + ((versionNumber += p5 * 1000)) + large=$p5 + elif [ ! -z $p6 ]; then + ((versionNumber += p6 * 1000)) + fi +} + +# get current Venus OS version +versionStringToNumber "$(head -n 1 /opt/victronenergy/version)" +venusVersionNumber="$versionNumber" + +# minimum required version to install the driver +versionStringToNumber "$versionRequired" + +if (( $venusVersionNumber < $versionNumber )); then + echo + echo + echo "Minimum required Venus OS version \"$versionRequired\" not met. Currently version \"$(head -n 1 /opt/victronenergy/version)\" is installed." + echo + echo "Please update via \"Remote Console/GUI -> Settings -> Firmware -> Online Update\"" + echo "OR" + echo "by executing \"/opt/victronenergy/swupdate-scripts/check-updates.sh -update -force\"" + echo + echo "Install the driver again after Venus OS was updated." + echo + echo + exit 1 +fi +# check if minimum required Venus OS is installed | end + + # handle read only mounts bash /opt/victronenergy/swupdate-scripts/remount-rw.sh @@ -24,38 +81,47 @@ serialstarter_path="/data/conf/serial-starter.d" serialstarter_file="$serialstarter_path/dbus-serialbattery.conf" # check if folder is a file (older versions of this driver < v1.0.0) -if [ -f $serialstarter_path ]; then - rm -f $serialstarter_path +if [ -f "$serialstarter_path" ]; then + rm -f "$serialstarter_path" fi # check if folder exists -if [ ! -d $serialstarter_path ]; then - mkdir $serialstarter_path +if [ ! -d "$serialstarter_path" ]; then + mkdir "$serialstarter_path" fi # check if file exists -if [ ! -f $serialstarter_file ]; then - echo "service sbattery dbus-serialbattery" >> $serialstarter_file - echo "alias default gps:vedirect:sbattery" >> $serialstarter_file - echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" >> $serialstarter_file +if [ ! -f "$serialstarter_file" ]; then + { + echo "service sbattery dbus-serialbattery" + echo "alias default gps:vedirect:sbattery" + echo "alias rs485 cgwacs:fzsonick:imt:modbus:sbattery" + } > "$serialstarter_file" fi # add install-script to rc.local to be ready for firmware update filename=/data/rc.local -if [ ! -f $filename ]; then - echo "#!/bin/bash" >> $filename - chmod 755 $filename +if [ ! -f "$filename" ]; then + echo "#!/bin/bash" > "$filename" + chmod 755 "$filename" fi grep -qxF "bash /data/etc/$DRIVERNAME/reinstall-local.sh" $filename || echo "bash /data/etc/$DRIVERNAME/reinstall-local.sh" >> $filename # add empty config.ini, if it does not exist to make it easier for users to add custom settings -filename=/data/etc/$DRIVERNAME/config.ini -if [ ! -f $filename ]; then - echo "[DEFAULT]" > $filename - echo "" >> $filename - echo "; If you want to add custom settings, then check the settings you want to change in \"config.default.ini\"" >> $filename - echo "; and add them below to persist future driver updates." >> $filename - echo "" >> $filename +filename="/data/etc/$DRIVERNAME/config.ini" +if [ ! -f "$filename" ]; then + { + echo "[DEFAULT]" + echo + echo "; If you want to add custom values/settings, then check the values/settings you want to change in \"config.default.ini\"" + echo "; and insert them below to persist future driver updates." + echo + echo "; Example (remove the semicolon \";\" to uncomment and activate the value/setting):" + echo "; MAX_BATTERY_CURRENT = 50.0" + echo "; MAX_BATTERY_DISCHARGE_CURRENT = 60.0" + echo + echo + } > $filename fi @@ -84,7 +150,7 @@ rm -rf /service/dbus-blebattery.* # kill all blebattery processes pkill -f "blebattery" -if [ $length -gt 0 ]; then +if [ "$length" -gt 0 ]; then echo "Found $length Bluetooth BMS in the config file!" echo "" @@ -101,16 +167,20 @@ if [ $length -gt 0 ]; then # function to install ble battery install_blebattery_service() { - mkdir -p /service/dbus-blebattery.$1/log - echo "#!/bin/sh" > /service/dbus-blebattery.$1/log/run - echo "exec multilog t s25000 n4 /var/log/dbus-blebattery.$1" >> /service/dbus-blebattery.$1/log/run - chmod 755 /service/dbus-blebattery.$1/log/run - - echo "#!/bin/sh" > /service/dbus-blebattery.$1/run - echo "exec 2>&1" >> /service/dbus-blebattery.$1/run - echo "bluetoothctl disconnect $3" >> /service/dbus-blebattery.$1/run - echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" >> /service/dbus-blebattery.$1/run - chmod 755 /service/dbus-blebattery.$1/run + mkdir -p "/service/dbus-blebattery.$1/log" + { + echo "#!/bin/sh" + echo "exec multilog t s25000 n4 /var/log/dbus-blebattery.$1" + } > "/service/dbus-blebattery.$1/log/run" + chmod 755 "/service/dbus-blebattery.$1/log/run" + + { + echo "#!/bin/sh" + echo "exec 2>&1" + echo "bluetoothctl disconnect $3" + echo "python /opt/victronenergy/dbus-serialbattery/dbus-serialbattery.py $2 $3" + } > "/service/dbus-blebattery.$1/run" + chmod 755 "/service/dbus-blebattery.$1/run" } echo "Packages installed." @@ -119,7 +189,7 @@ if [ $length -gt 0 ]; then # install_blebattery_service 0 Jkbms_Ble C8:47:8C:00:00:00 # install_blebattery_service 1 Jkbms_Ble C8:47:8C:00:00:11 - for (( i=0; i<${length}; i++ )); + for (( i=0; i Date: Thu, 25 May 2023 12:16:12 +0200 Subject: [PATCH 207/209] fix Jkbms_Ble error --- etc/dbus-serialbattery/bms/jkbms_ble.py | 1 + etc/dbus-serialbattery/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py index 03a6e47e..276103c4 100644 --- a/etc/dbus-serialbattery/bms/jkbms_ble.py +++ b/etc/dbus-serialbattery/bms/jkbms_ble.py @@ -14,6 +14,7 @@ class Jkbms_Ble(Battery): def __init__(self, port, baud, address): super(Jkbms_Ble, self).__init__(address.replace(":", "").lower(), baud, address) + self.address = address self.type = self.BATTERYTYPE self.jk = Jkbms_Brn(address) diff --git a/etc/dbus-serialbattery/utils.py b/etc/dbus-serialbattery/utils.py index 8769d5a0..759834b4 100644 --- a/etc/dbus-serialbattery/utils.py +++ b/etc/dbus-serialbattery/utils.py @@ -35,7 +35,7 @@ def _get_list_from_config( # if not specified: baud = 9600 # Constants - Need to dynamically get them in future -DRIVER_VERSION = "1.0.20230523dev" +DRIVER_VERSION = "1.0.20230525dev" zero_char = chr(48) degree_sign = "\N{DEGREE SIGN}" From 81ddf5736dd43433094799beffb2d80befb950d4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 25 May 2023 16:04:43 +0200 Subject: [PATCH 208/209] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3497d8..910781ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ * Added: Show TimeToGo in GUI only, if enabled by @mr-manuel * Added: Support for HLPdata BMS4S https://github.com/Louisvdw/dbus-serialbattery/pull/505 by @peterohman * Added: Support for Seplos BMS https://github.com/Louisvdw/dbus-serialbattery/pull/530 by @wollew +* Added: Temperature 1-4 are now also available on the dbus and MQTT by @idstein * Added: Temperature name for temperature sensor 1 & 2. This allows to see which sensor is low and high (e.g. battery and cable) by @mr-manuel * Changed: `reinstall-local.sh` to recreate `/data/conf/serial-starter.d`, if deleted by `disable.sh` --> to check if the file `conf/serial-starter.d` could now be removed from the repository by @mr-manuel * Changed: Added QML to `restore-gui.sh` by @mr-manuel From d1fe9f9057f09427585e2aef44275ae9a9066c7a Mon Sep 17 00:00:00 2001 From: "Strawder, Paul" Date: Thu, 27 Jul 2023 16:03:30 +0200 Subject: [PATCH 209/209] feature: Allow to control charge / discharge FET feature: Allow to enable / disable balancer --- etc/dbus-serialbattery/bms/lltjbd.py | 73 ++++++++++++------------ etc/dbus-serialbattery/bms/lltjbd_ble.py | 11 ++++ 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/etc/dbus-serialbattery/bms/lltjbd.py b/etc/dbus-serialbattery/bms/lltjbd.py index 1355e084..53733c00 100644 --- a/etc/dbus-serialbattery/bms/lltjbd.py +++ b/etc/dbus-serialbattery/bms/lltjbd.py @@ -2,7 +2,7 @@ from battery import Protection, Battery, Cell from utils import is_bit_set, read_serial_data, logger import utils -from struct import unpack_from +from struct import unpack_from, pack import struct # Protocol registers @@ -144,25 +144,25 @@ CMD_EXIT_AND_SAVE_FACTORY_MODE = b"\x28\x28" # Weak current switch function -FUNC_SW_EN = 0x0001 +FUNC_SW_EN = 0x0001 # bit 0 # Load lock function used to disconnect the load when short circuit is required to recover -FUNC_LOAD_EN = 0x0002 +FUNC_LOAD_EN = 0x0002 # bit 1 # Enable balancer function -FUNC_BALANCE_EN = 0x0004 +FUNC_BALANCE_EN = 0x0004 # bit 2 # Charge balance, only turn on balance when charging -FUNC_BALANCE_CHARGING_ONLY = 0x0008 +FUNC_BALANCE_CHARGING_ONLY = 0x0008 # bit 3 # LED power indicator function -FUNC_LED = 0x0010 +FUNC_LED = 0x0010 # bit 4 # Compatible with LED modes -FUNC_LED_NUM = 0x0020 +FUNC_LED_NUM = 0x0020 # bit 5 # With history recording -FUNC_RTC = 0x0040 +FUNC_RTC = 0x0040 # bit 6 # whether it is necessary to set the range when it is currently used for FCC update -FUNC_EDV = 0x0080 +FUNC_EDV = 0x0080 # bit 7 # Additional GPS protection board is connected -FUNC_GPS_EN = 0x0100 +FUNC_GPS_EN = 0x0100 # bit 8 # Enable onboard buzzer / GPS protection board buzzer? -FUNC_BUZZER_EN = 0x0200 +FUNC_BUZZER_EN = 0x0200 # bit 9 def checksum(payload): @@ -290,6 +290,8 @@ def get_settings(self): unpack_from(">h", discharge_over_current)[0] / -100.0 ) func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) + if func_config: + self.func_config = func_config return True @@ -345,34 +347,33 @@ def force_discharging_off_callback(self, path, value): def write_charge_discharge_mos(self): if ( - self.trigger_force_disable_charge is None - and self.trigger_force_disable_discharge is None + self.trigger_force_disable_charge is None + and self.trigger_force_disable_discharge is None ): return False - charge_disabled = 0 if self.charge_fet and self.control_allow_charge else 1 - if self.trigger_force_disable_charge is not None: - charge_disabled = 1 + charge_disabled = 0 if self.charge_fet else 1 + if self.trigger_force_disable_charge is not None and self.control_allow_charge: + charge_disabled = 1 if self.trigger_force_disable_charge else 0 logger.info( f"write force disable charging: {'true' if self.trigger_force_disable_charge else 'false'}" ) - self.trigger_force_disable_charge = None + self.trigger_force_disable_charge = None - discharge_disabled = ( - 0 if self.discharge_fet and self.control_allow_discharge else 1 - ) - if self.trigger_force_disable_discharge is not None: - discharge_disabled = 1 + discharge_disabled = 0 if self.discharge_fet else 1 + if ( + self.trigger_force_disable_discharge is not None + and self.control_allow_discharge + ): + discharge_disabled = 1 if self.trigger_force_disable_discharge else 0 logger.info( f"write force disable discharging: {'true' if self.trigger_force_disable_discharge else 'false'}" ) - self.trigger_force_disable_discharge = None - - value = charge_disabled | (discharge_disabled << 1) + self.trigger_force_disable_discharge = None - cmd = writeCmd(REG_CTRL_MOSFET, value) + mosdata = pack(">BB", 0, charge_disabled | (discharge_disabled << 1)) - reply = self.read_serial_data_llt(cmd) + reply = self.read_serial_data_llt(writeCmd(REG_CTRL_MOSFET, mosdata)) if reply is False: logger.error("write force disable charge/discharge failed") @@ -406,18 +407,19 @@ def write_balancer(self): with self.eeprom(): func_config = self.read_serial_data_llt(readCmd(REG_FUNC_CONFIG)) if func_config: - config = unpack_from(">H", func_config)[0] - balancer_enabled = config & FUNC_BALANCE_EN - # Balance is enabled, force disable OR balancer is disabled and disable force disable - if (balancer_enabled and disable_balancer) or ( - not balancer_enabled and not disable_balancer + self.func_config = unpack_from(">H", func_config)[0] + balancer_enabled = self.func_config & FUNC_BALANCE_EN + # Balance is enabled, force disable OR balancer is disabled and force enable + if (balancer_enabled != 0 and disable_balancer) or ( + balancer_enabled == 0 and not disable_balancer ): - new_func_config = config ^ FUNC_BALANCE_EN + new_func_config = self.func_config ^ FUNC_BALANCE_EN if new_func_config: + new_func_config_bytes = pack(">H", new_func_config) with self.eeprom(writable=True): reply = self.read_serial_data_llt( - writeCmd(REG_FUNC_CONFIG, new_func_config) + writeCmd(REG_FUNC_CONFIG, new_func_config_bytes) ) if reply is False: logger.error("write force disable balancer failed") @@ -426,9 +428,10 @@ def write_balancer(self): return True def refresh_data(self): + self.write_charge_discharge_mos() + self.write_balancer() result = self.read_gen_data() result = result and self.read_cell_data() - self.write_charge_discharge_mos() return result def to_protection_bits(self, byte_data): diff --git a/etc/dbus-serialbattery/bms/lltjbd_ble.py b/etc/dbus-serialbattery/bms/lltjbd_ble.py index de995492..27d7e927 100644 --- a/etc/dbus-serialbattery/bms/lltjbd_ble.py +++ b/etc/dbus-serialbattery/bms/lltjbd_ble.py @@ -179,5 +179,16 @@ def read_serial_data_llt(self, command): if not bat.test_connection(): logger.error(">>> ERROR: Unable to connect") else: + # Allow to change charge / discharge FET + bat.control_allow_charge = True + bat.control_allow_discharge = True + + bat.trigger_disable_balancer = True + bat.trigger_force_disable_charge = True + bat.trigger_force_disable_discharge = True + bat.refresh_data() + bat.trigger_disable_balancer = False + bat.trigger_force_disable_charge = False + bat.trigger_force_disable_discharge = False bat.refresh_data() bat.get_settings()

    PsHAvyP6}S^#W+$>e7x^alFcFqB=qe5xl| z&^|=?L1p_2;H`i*Or$--Ph~O!z|_WkA)ClD25fXzXU~(fwt-OaQ7Ej+ACDjiClJH` zNgzDf(KH>{xpnaE$s5)yw5VcZ4(IvqNL&@td#gtP^`XR%?{TeG9G@BQ1k$x|(0XIK zCG%wLX$a2?wFjR{oGH~0b|YNO1jyjW@(VM25$^G~H6aa^IaY)e`CyeV*uGJ}T&+gW zet3H`N@Bf1M@3bYfqTN|VAW|UC%$vE8k*y=HD6&j!rx~9!(ApjJo0FBYb(cjp_kuf zQESAZesx)5ymB3QP2>6ARBnm2T8qYo+TUMD=Q=pjW2Ssh8hhcT&BvZ~`}4_GbDgm} zH3br)O990f&Q<7SI*z$sZog~qDzKYmvk1-n(8suLoz3h+KEm}-$(<$tbye9VQhY{$ z9lar!q+RFM>eu5cd&O0K4mT8+`m^39-6JNe9(Ua|+&RtUbpbF;pC2``$V!WA;Ve5O^Tj5gJ z{NiKr|5>sd5W3~Ph~lTOT{!$zlt{bED1Ju*+fi)!t)o9q7^^9@FYh+#VZFaxSZX)Zi!E16%}i>_}#e-xrfXe-N)7?|UX$aOD_e?#udPL@0Z@^-ld&Lx+{!Ei@&Cae= z_QmOFh_$#p%;S*U>K52sMNB0v=_@EGI1QV>?MSpvvuQf%S7huM(l_HV^F6A@wTh&9 zt#OJ>xJ0|Gj#fXrJ2yASZ!^HURgFYhvXba)A@i8gVcsGe`wn(vA3;%k093Ewj@tgsBKa+ol}?yf>}KP`!_RAs@D zJ1ApyrQj+PZEco5=@akqrwabTyWEilH~)O2(rjPs&zb{r39bGZ(uXf=>oczWVA94w zOB?li!I!DT!IoN%fRD3*HyrK`4vD+17(msQC7kEgJPtS7s7wK!=A# zDKUdtL(kg2&W3Z|LilB_ouiwZYa-Uqs@1eQiaZ63K;Z~PiMO73y|jFUi#?O)$nQUK zC<&8T6J45YtZ&#JP~F@f)J(>!+?(_YIi_|#{IkK{y+~GELObHr#iRbbKl#J)(LvYl z7!3BM=c8`ntl0@R-ms0|V{F8$;%udYM23M9=3E0y7i1i3n&ot&NL z7Ses=7~Ff{ zM|>s8?S~KqyLu9+_A{p3&HrId=S8JwW@g=!6kA5_2Bj!)b#yGJhi~^+4CxsM9UH^S zgi@CGgkC%pCVS(ld6`9z_vE>-pE@BZDa>aZ?0u^>q`$# zuOCj2MEa1Q|A~e@K`Lnxs}(HXCh!CFK4Fc^q}%;RTKjv=Cp@JMo57c8`M+6Lt%w{a_FXg7jjw5vUP*bxqcsj#nguG& z`~7!<(K0d5L$)8Gvz{{zJjwk0zzi=#BovE=PPmYDkxL&k>Ex;vh{1GqV+~hI@GZZ* zCf0uR$h>LqX8?JUbK4lO-8Qkr>CB@l3p)6qbzW%@_6g!bq|Z`x^sldP(bJ-9Z|XZ8i#9GKdG5vGeGQzR9Yhwn08>-bD1+QAss zSZ!A`^6c4X--e#0y48el5}GEmIZeW;KSU?%hSW+sh6?r3*!}Mua@ZKnyax(3?S1XQ z@!F&bd;YTExGgFP%Y?F|y-nT9+gu3Cn4U#g+wQXEXSYV`0i^x6ukTg~ARH;zb&-lU zJ+cjxe}mN|)l8qKKI*ENEL#c<>zxXpbg}IR0nEa531b6?n$-|CY( zDDtSt)ahqg7~*^q*?g>+F8B5Q|>S7N*?bBW9w;2wxR1 zaBOhq@VxhhJ=yMQ+VaxXFYUR~+!Cu*(TSe{{$g!)|xJuMs%sewdXk;iSA!s8t& zQ^u4Z8BK`Dr9nR`4bjhPJIIxaE;Z4if+zi&qnV*Pj%&j*Da#p@OIguSHYB-&e=v=d zoBso@-sDB=j^6#OaMuH8MJ>n8F##da>7YHM#7zY14!`JL+{Ve9hpxWelO$B=v6*Q$ z$Z3XbvizPQJFl*-Q6nF+v=hK2bhv1o;3E0v0*y&@CX6#=55m?j=E@Zj7#+fkY`>#{ z7z>jc>K92=Nc-_aC|6ztQ7$sK1OI7ZXGXR2*@0}ClnXmA2*<=rkqySw_|L+@b*)fJ(c9o!!89hSGT{kzOB{|KvF#%mZx8S6$SU4V~w zVvFu4irNP>NV&QWG=Af@LCV~0r;3${>p9Tyx&qmb{frf6g(t`JHlUj4mm%Enryq38 zS<=}{eRI$|myzjBVt4xW(AJHMRdlH;xgmQ~o*16Wxar?t$wf4Ew?m(b&nZl`W}h_g zDop)$?czZsRNukI@x%EuLbi8e8~B?3WYRmeGe5)kfUiC`+tK~z$!qeY1a@`Mm9Noj zFHGXSrGxnDw$LNS6;uWw&jR}K@-U$;MyWo_bxo@uOL|u8RtaO}XKFBZh*nd=SjfS* z$y$+bLRVISePLPrAV=dM5kF}rEw^UMuN7&wnaUlg$QURJ>h9~SJWhziG(AeM(K4)? zGz!-@H$9QI@TeK*x5=4Fo1RO!^ZOzNguv_p>;kBf_NOgFLv{jLHmhW(0<%e{Tp5%s zoew;p+H>p{1P79lv3klejBJl%ugaJi7D4a1JUY>oK7wq)O%6*%YQ?E zpw1s*m!FyBm%`vl0@fOx{=unDsG*AzLS`wLKuDc8|T`@gy>)#)0|G zn>TB8BtQGHZJl)?aUFFttJ1okyAt*B_(Ez;*z;fKM7=-fcj4;y<}i#n6PYV^fB^1A z2iP9VIFN6eG_KPQw;N&5oLOnP^50zO3=Bse)7~>LwwG^6@fZo**$*S6PN#Xd#{5$R z-$bUglm*pC+K;A^@JO0Qlz(RgJ_Zz%qZ(3g7Klt@Ev0otQiE3o^M6`N55Jj1f}H#G z>6G95j=xunj5*X7du-9*9u69^TTCX1xd^E_^dknJEM(hDud8ZpIJ++5h$sM`q(CUI z<-R2Q1~_mMmi%FKXN8ZL?#R?pspL*{kxt#x#n-WJi&35IHl$~0&<;RYJEoy>(*GO- z#vz_+o`S^(oHAOD`g;B(vMn9d@>=Dk3R>l`mb6pI^cq(lEh!?T_79HcewXpoEF|OU zRIubJ%p%Ic32S&NEQM@K(>3rz?R8qLrBDII+WHDg2oaY?36p;?oCo}H@5%$%2ck_3 zK#cBIZ21(Bl*h7!)CWdQBwSFG-srm6+3;5~Dug6-b4sA2U1fCO;e`na9xMCvADI1B ze*3GKpciWgMfSEUK;>&;M_23D&bM9Cwlk5lRbFv$t+5hweFm%wR|$S##`M|(bAP}0 z&n%IWs=qk?l}tVe_-maQD=iP3O>K|aXaymL`{sElC#CebYH|7l7pC2Z5;bI!qOl?~ z8tD`-m*z^MhBVLV*ie;9Yc!6Bb?~)OK@PQvv3{pO^c4W2Yr%6vp?j`W3!QzW=ys|$ zS0RR!YM0`a{cgLU%iL}Eh2`AVj9By5QLQUkA1sf9gOg5B!cytgA5lGswXIErDs2H5q4oC{Og~Mx*vg$)piF zVcC{zI@%IfAW=_em`;s>-x#OwW8mYwqz#xt^oruS$!`}A)wS!y)`zd$F)(hw_bfHL#vdKhMgSOf3wn3 ze-%t_UD$(%tmE>iGRtME*s-a7lgUIR%TgvN>)Ig2$yxJX&ZrcgnXeIUISpVrmNX3* z$LQSRyPA6ZuA#wFkLp~y>1hY*b5^T4*GllL6(!&Vb49KvYwB7XF#AQ?)5F&E_ppW1 z$SQbc>mcHaOwBN>kC6}Oq+va!iPJeyuFO2Y7Ab|G`&L1d97Fi6OQ8sT(|+A;X9LIO zxwYl(zJ5aLlOO(Rr&pgSuH%k`wui?|amX@WtG(Km;h% z47+k+chrvPshb1Au{%l^kxHEW=dtpng^ceMnTNS~&P+s@(&^nieEuP&K{cqj?~x7B zy*yL0{=7iZi<%I+2Z?}U#2G0CKkniF!n2`>`ZQ1Hs=*tuhG$*xh@p*~f#Jq%`G0Cf zUH-NV;Ndre9C%ze@CM9UWb&ohmXo_HcHy~yu&=GG=Gi5Z_c)yv)NZ)B&4k7a@VV>f z`G&FQ|H-?yP59U8_ihq*tW4Usj0|}=`DhO$x|agyp40kv0o83EJyY4 zs$f%6S!o~bhPBvm6hM5QUI#`;f9xT6X1u*Y`nr+hREoFkqRFlNm#3K5s)Gr-~&M3mu&HjVD-b@1S_$0x$gG~uuBB`{NiV1A{O|qrBZxJ8KxD`LsF)n zuyIO6{`3QebqAq0B013OJS}Q&%o&l0@n&SP$sdJH$d7be^7udHc zAO;(*<-*UL1=vSo^GCFl1X8%mWJOFa7f%(#gky8H*s$+p%76r)OBfqZFK#jM05Ph{ zoV5x0RY2H&H219RbZOZS!ACkpBUwDZZ$O}u%l{_OHSD>|Q@KGa1DvW(gLJ z&vyrs>GH!SaS7OO%N81Ha}ZFlAMNy z$Md{K>A<-W=8G|6PpUe!5_xhmU|M&5Z@QSVK_s9N=`*gYi{@ih&}k@b2Z~|lD+~CwA>V?3tzI15`x2dgYJIlvr}Vuh zr1rx9mOEw_V+8?(xXpYAxUglmr)S+#6z_mtzfk`z{{{+L68wYFU#iX+WFp+t?$}1j zO$Vt&y`a}C#gol3WpTri=r~uG2Cl8LQYwKo0h1>^d*CtWR zCYV3hlA?f!i%z>$&5)~6^+vLkdSbniFgNE520bD!PEN;BI_CWB&@`%3;+*0!Q2z^P z{Bl}78CPV4(_lmxzgs8&VB_aTxn~&WB-JBxUxH8n=9x%dKt(bdk z0WYW^P29#j9%~$Hq2aIwK}|tYAQ&+bcW1vC+0Hk7~lmrP|s9-Ry29Di|4QMTs3*u?+RqV zltBD%?0hBj8`lC?n5Zwwp8t|fwH_RdQv)G9b4}Mq&k=Eb;ISXQ`0(r+>t8WH;~Ze= zGt-@fjOo}rAfc(f@Xy5o<5Cu)Ojk>{?GLmM&lo71OgRn1ep}<~%vMleurP7pX75ysXj;YBEPc85hs=2b>{g zZIlPCe-pLeiduI2(-y~)sOc~e}X`N-?`>Z ze93|1-Ck!IXo7o9-Pau{`=B!4lgz=VSsaDVE7^7UUA0mG%tyx**I|h}1S!BCU!a6W z9+@;KM!MLWvbD7Euz{;(PEJnI_MJnH;uFvbBnjZx3%U)`*br*dHo@HA1sT<}l%4TK zB~OolEO2pz<+hW%9?ff^R-fiaqV#ottp+XIG_r7Onx@wHad6rcKd{x%&{7V)ReUOO z#3S(F_go?Y3jY~o{@Q>)D2)=k9W6={{6~H3)TYvLl^8t=WW=JWB_1CASKNC60%T4- z6_?!U-TVLI#{Qcv{@)iS_g>*rntGScsBv6IkzmP*|_|*Z;1(hLq+N z12Gr=ZAz0Sg;%^b;jxy#@S|7Cam+-4jsD7?9S;jd}?$^3-{oN6L1r;1wZ+(rY4 z@(zA{e*(C*PZ^v$|ac^olCtj6?6D9ka3HOLpNI zQ5x`G&^&^(6&c(XkkEiFfP}rtpgaL2%kYUu*ME>B#t4VhZgb*r!`c@gVgNxhAZ&B# z%!e<7H0=irg1%YMQ0I=04h?tn)Jf?qrvTJNKf(P+$~&4OtEZVu1U_yL6x_U=PfSK^ zmxex+*K4qff(|)PJ;aWaQS^^+CP^M}J8h=d@C^M{;r4wWR4x%dSUFsj7a?1^tq|%o z?#kNRuK^35c+a-GZ4S?LLOEAjRz?wsA{tP4h9DF{#!i_42{;A48E$ zl8^4edpavtKT4Ds)yuXT4eo+Gr@LS9Zk~|i4^GDQZYpX@Phv`2YmCOiIBQk|K0J~!{u-f&t&z|8K*FBSyFeb zun6L~F0rEuTbpPY>FzMl77H6LuOsFpq_&oZ7CmyDZ<#!`<=42kn3QBYI0QFd=GJgn z)BW6IFn{uh8Qm<5nAhzB9hy$gzMH+p(Z}7_IV>Cn!jL5w5X^v-Td@w!nC=c~Jkx(F zPXEdilUFfhK4Z^3*2E)!KIahGa|j5&$;i;qs$uxdQ>fNGBs))8fxN53U>ucQ@^ta~ zUd^@d9Q9(fiWLjLXB^dzHZ61)&u&@ZnpSGLBjQC`);#CECFQwVDT%ppspFcjNa_88 zYWi{ucc*~#V8_~OO_zpf$CpEIT|j!I{8oJ7j5PX*v*GiXz&ZZdO6gIV*eMR@*5h*c zts?|Z?88HF=y!D!u~z@2`M$c~4#jhWeTOyMHdqTwer^5DBTqq1$e|G}_RXn@hT}{$ zIJz_0%UyEh&+|OrMsl1d2!oiB|BuJ@{|%1?Z0judF_GhxLRL66#>Do-UZFybI*8q| zAnp>iqv6Gq08Iy4tjM$bIa9c(!o~mJqskoOeAldh5Vt+z)LH*e;X%whnSG!+MCo96 z6}|VenvNXZ#XEK6Zodf1ZppIYDP)G6n)V-cVA@SAH0zI8O_KGM z9UWCW`ud_%QXcPxviJLI>=_|S9>m=-IPFY52bJ=0`5=!6#A05X-ra!a-qC&nQZ-kAL;6w5k1j$efVcEHep*$0ucg%C_c(FULmRh z0uAqE?VSmv00g?`rV%Xj5GT;b@k`fE1^NM^*=cz$0(3fk8Tvm8^cz(QF+Hbfr^71Y zezhYu7$U4{j89|0=fQ7N)2=#IeCdx;)cQXN-j|tCNdsn;`jJ)83fbV0kh7dsXYg(@ zp-~2~v!YXGCo<7@w!LS)m}vWD)ard%Hbb$nGx!&PJCq^52Bqv3d%6FS{lFrwJ>V!a zmy7LA7SV6jq@g|ey8Jz~vR#gOGBmkII z#S5J~ecUGr;Bvjjye-=8x<D#QfW;EvBO#$`YIGu)WM1;jQ&eJ z2Wn7JIlEdn33ImPYg3DjBVEGXPruy`-@RN*c8y-7(jQ zxOTbK|5-lvs99z1bW}YxX`$Tk3l@wq^)x4x}#&Um1fTj1F`i@?++@^xH+Ij+jcD z_S0fY4Rv4=D>xj>O8M?ca_h@*(B|63bkr#P`>|KYvDfOOt?KyJMs`^9$-R6jTo%X& z1pK!pnyoH@I}zTyoKDLdUnzG_5EULy-8MAsFe_)c&_nS6Jc3DLd7rnUl5&~SVc(9^ z5;vREXMb8~6xv8hNElDjZpFAPe{Y(Hii9`l-}F8-%rkl7OOEq?kN&aKIaYo0eyk(l zZ5H}do$=9mN{?n*ojx@kWoPS_)1u#|`*60UI+`1`wkuze$@q1?;WC$ard+tB3A^t} z7VS8Ai2=*rl#g7oA0>pYX)Orc9d|S%{f8acj0gzvd88-Vg{(aBJ?B@W>L0*O&g{@~%$;r1!ukl8POP{E$`TTY?tbM7>6LM~M zL`Or~yf&r=#x3xyH}2%nJ!SQVctn$X8D=9M@1>=E}4ih0d$ZPDbwU^%SvOj=0UtC|kFDTYoZy$A0fX&RozW zx_+q9$lwD*x`BktNVD;=*UQ3m2?rm%?G(ALx{miogD=rQR|uaQ3SoB&$x>))cs zaOO|s+Oc&)t|I8r(Ul`c;q)+HUv7pO9x$lx0`d;2TX~(X zC?tZ>fW?x*5faG*Dr`L9Gb4`NZJFnvBQ0NpwcPPrnQb*o=a&;Gs^ly zgdQMGLFkG>+V60c3y92rI?8FseBO;RT_yn{T`wkzPxHB0Q^J(iLggh^MfKq0qSzCj zPan9!;DZ#)@y?LUG$W2xDx4d)p+ZCrv@NjLyAPK6AbXhJEA@<^1q^it`0lSf_ zrsi(h=N1>^zyE28tfx1$Qvj6+vz98W$5jZXD2ud-IV>{`u7GNg5}rDN2j2)`%2gxy zx`#kxHzqjnf zRJ(Y=bLAvyDBA6D&Ay~Nnmfd_*v}Anj-rAFe#DXMq+F}_F5-OLP4@du?VRzb$*HEE!C!R(;+2Yz~`4bm(0RK(th z@dpaWzWMgB;h?2#QL)a}xSp4|`GTv$G9R|n15JL)6LREAsTh<|V-$6-UdtIR+{}on zHbR&iPf@bR)3k6Z0~+BCH#W#|w+QEnN)LU*RW?}gsvo+6MaVBMW+52Tc}}G(sTbLM z3jqBK1)pHy!-9A5BNCcEGb?9Ua2%I3ows3!!tFe=ctb(fgQ5VbMpk*!ME6UM+PG@2b^&S1|+r(g+SgL;tqT9E zR$whP>KiVPUOCrSXy;Ta}^pdoQWfD+kQzL-fO3tKv5YSE}pEkogCQ}Ifsc(Tvi^CMW>d~ zJ4W%N%xfcyZ+vEcNR^R%sn$*Ym#l3@24P~{?KeYB8BC()HvSelCR5KPVs*nYLaIN+ zF(N;mq?!Jj)^FcP?aLZqL9 zn}w6BUdwPecaNOVCSjn49}|?Q`Ms8bYdtF&KgfZU=`r9Ug552@o{GP{>P^+vqK^9R zL#h8RmU8%sg_Scty}7yhK}b$?sp$iOA*?x-+C@kU+!%tid{xuDZ`Eplu`c%b7Rb%- z7j|l=k6F}ZTjQ9_tjbT~SB<=o|62^60#!aEQyGXk?8nDZMw-H|6+ zk8yO+n$|FjzXS*9RbTD1LVt5!mQ|enqoE7=r9PyMkhP70)0%>v*Ey*-?;aG@94>aq zm>G3cwS_dFc6yy%Y;xnWIl=}gI6!~@68UUOG zWQ({GM}Zk2@dV8Ij}kY}M6l7fgLLW%E^&@;IJUMFqBj0pgZLq9tr3a=2dI+;E)pQh zHtYP{=lcRFuDD4!WX)PK_&(A>(mnDBz6DEBFK$n8Q8nVaBM4(9s3+&Gx zk6(JIl#o9iY^8B7$UCoV`U)1rGd>PGF4y)tcjK2H-WjhBqW$f z3c-hUGLx>LW|rOncyQM1l#*_?;%dPnC_MsB*XMAMd-%jAacZ(PoDE@?;FkIb0}D_N z3#)(I)wYTwR+4Z6{^q*<|4@I8>3@KpL_|asiV8?g zBBE1pA|hfl*lF-j(-nv3L_}~RMWmFryYW&g$t&9CzTnOY=3%}7cmJwdMpn6(Ra}ks zi=$Os2T!?_-c(V+mzRs#p$h&#DGd`3_j!M$3_h}O^Q_35LC=hGwX3@I7Hs!y_2}iP zx(o)cneo1a5W^5e5K4a{D4YoVgrA4DQNlek?$P|`FTz#-EB-`};Q#YRB8Uh0ua(i} z(zBCC|F%L2fBC<)=l}%`%BaZvSA#^5z#D}A{#Vyi5kwGnhkR?;{~8Rqi15FD`p>}r z-55|uh47!E5Tua$_s^eJV3c3}YZ&loVF)a`E;E<)fA)bOBK~yY-&FrTzb;2a94p*& z8~R^^fg>)Q`mgaKMqzNodT?^^e+`Bh3N!n!VNkX(5J0A7GqQne(!+U+z7Nm$ zq2u(?h9oCbKqvyU#RsO$u|qZ4N-axr`y48hH>H&z*@|!F`abMSlKjWSjnu%v%y6QB z`w&Zq?oYZ7M&fbtP=)qHzCqWffq$NTC<1{|o+g39DU}~aY;7(T@x425n9YvR$J#vB z)je(W9}}ww0lyLx6T=#*t+cUU?eXtDD%CY<;Aq>Bw;z~JdJTwW_b0lli0!t9X)FJ% zKjH%+L-nr-M;YJ_#^Rnc0j8YbE{l~%Vkdz)1;U8cjq;aN1`+s0;;$O_*FgcAtl&mE zwQe<7wkmj7l(#tWUogXgGY}6A&jnD!!9B!W-LilCL1Y4No{>#+Wu-I*VY0e+y8Xnj zvT8)cnj8Gtrzl%Q!NYwqnymjkDM9cyF@!RN3%K*%KX*po0u8*$%rS-0@_`4sYR-LT z{TtZh;6Ad3s8UMEE|^NI@YA6af5uaS8_7gcilyPQ;9**+rx*YA@sSLUcy?>@91NKX zR>M4VuHVTdA;3|zloBd3@N9w|T+UjY#D5luX=)OIAEYqzB=AV>-2#e#cD?|-7JLeL zO${CoF>i~%>VD?mB?y&OcdkmGzMQ`~1qYoJ{3cYI7lE1ex^E8eKJf*jjM9BOi1aM6 z=C#pJsxVqEFzBay6on_V69HClCDY{zB7&L%V_rD-Z@Fj_Lh1qtDMK2tP?N83%KTjf zVIf42?2tD>1(e*HVAgCdmn}}NI7$Jxk-zI9BgD<$c>Ej5`5;gpT?dw~CKyog(fo_Q zj-{mnW1$iu*J4#CSVMOH&e!0-SEil;8!%`W&;U1aP+Ak0<0J7yJmR%@3heoh3;%|g z4Qv(*VdoDZg5G4Kd_e5@Vbo`--09e?(x$guN_gavo8;W%NB7>AkSUF#kP*LI`xJuMdKUcEA!$h&vu zT#`(@3RT|Ru3rBwsB18IZn+^3J$HzG(9J32aOAx|-9NUpHX}D6pYHp*f*Gn1QT!7E zwrv?m3@yBw$u~OHr>*SVw_%UpOI62jELnW_WP|goz|vbK8+OABVx~D62fjY=!+YWv zHOMSppoFmD9TH8V6}AQ>iO81fBqQ5&?=?Y6R?3SI+oGXvWA}};={={drss=uj|4cD zW{AC3?DgLGSxnEcc&`UBIUdXjIu5#qt1K?36f1d-@b5E?LxbDCFcz7eclXP4neTb9 z?8$fbjmO7?t>G3un!X^Sf-X8kut3Ze;F^%^qN1-I7u+kb(zic6dVRwwk;$d)cyVFH z_bVk07DPuGl$tCWErf4b#(6}~t*C#RN)R5atQnoM%v-{g%TX{6WXXo3kEu>A)w1{z zb3)mq9wwym4hbf`F&pzfj;plA4`J|*TKy&S(2y)<0dbFX!PO&98!sx)1ZQZ=6*UQm z-wfCCKdq&avS6=v=hlvm_%7vOx-kYd=_R#G%w9jsEbU&Ph2AglOLiVE@2C5giJ#NQ zs56*Q!ecI^IYS_soRk-dvq*PkkHvdWZ>-Gtt0mv;Jtwwyk8u%|mSb-sYfs5>nkq8@{R z60eD3?)d%OvPHd*=zA}=!eSHFSJ+5SdB|ksJ6aBFO#n)u!A7M(kNGk8~bx}O>NFFyGB?ogmOMCYicm|fG^nf_W$~t znqu@kp8`U{868F>Vl^gSH{95rN-6pKQ{f_xA^nMNGkLVJ@wr~7^J8-oUZ2|g@67zL z9qufHO#LpYck;OZ-l7wu*0A#>{K;LER^GS#?9#Ec+Cd$LwCN-9sTeL5syHUh=+`5+ zA+lvR;W3Yfk*PyT4Jm(G_v>AEp-TRUF7oJLic14%Jrg?vhx#(7{?4~@`0-Pn<*izI z3I6-DY|t|@VE9(D+aEvnibJvvQ{TtmL4{cn;jKy%_Up@8H^{sFlOU1n@ibE?- zeo1IBsBP*MK057QxL=0In*~G=CdQhLvjn~CDA+c*s&%5w`wl?omVOS z5z8>(L5?~J#~F&^kBq0!ml>@1mMTpqrTtECTZ$1|_Q^jKvM^kJ`PKqP>yL1OAPq{G z>nA;vF8Gd4_YawMp2co04YAX~BN&CtD{<&>o;XgmN)tbp1>aGt!%DPYgVEB+dCAH( znj=S-H{Oen`kb2Vmw)$2Oc26*zuY-aJzHhi~d1{WgUTNQ+lqdXyeh_|07r8!dY>>9?U46JWFe?AhZ6gq3` zP@TeOSi$Y~MdKFq3?f66m~H&}MlmDmpi$9vuo8P`LBFU}dYb+!(`T;*L)b$|Kl$)M*oTj^n&qYPYw zdT&z3zHGC9c^Z>=dHImS#voLw!N{$2;aJ4Ova!%%=;|?!U06gAz3^2BTVw;D@>O2nK$Qa zX6^8{SSF?ql3&!E*^s<ln(!J;u=X;V+Oa=OrT?{Zb0ib+IYxSEXD# zpGOt0XH_g3$VE`Lun?#6mb*qe#CibFy~!>S zi&)`)VcgO!wkFQMUmA@%633yQ+eg?%RVEK98P{$u#8>Y67u9&um^X{P zz(Xb|n;ar8D{=|%nYZ{#IWfZTk$z*1vYS#oqMjrK+9b8V<+Oqi<@3yI&PZ7<6&^dx zU+)i#d;@X>*AK?EdSSFa`5`Uj0yz65V<+VwlciMfu8ZHPPn#TyjMY@EVkW=cw-Y$N z+fNpHj6E$ROh8b&UsZFHf=4(qB)>(~N7NHy(bC+#x8)D`SrUKk_Oc(K4MOI-C`w4>GA&bYvrtu96wp0d1Qm;tK5??TfIBG?WxNyN0BJI z%`J+>2?j2)@hS4`0SUS0+NO~>g)@}ZNO?Nrr$-lcYh6*b>wEa6(HBX$)zE)n9N`Eu zx4{-dRGn+$#UQ&z^2}sggArS5 z)MsVW8W@Gq;akswA7OaOSW#TzD@lvTYb7MEyS!S4z4a|hx(?2Fjl9J}i-=-)T}SPe z4Jv1p6vwvoy_Y|ko}eRpfI=MGh0`nbk^-3pE^CUH@%XN%hY^J?IkDY{zNfykeYLzR z*E+Qgb^oaEon`$IKG;{H+vr#Ls{!jnQJ_51_jh8Qmw$ZaSUDg#V@xVWe)CA8s&LuAB;5} z<4cqcsG3ETU%CCXf@4msBYV*~_`8X$suoLKB$_W^tV~BP;2pm7cYGLf^z>7t*3E zIPZ4xCSa;Loj~(nFKPk=^@ka`37|&I)5p^{>iXTeGc7w~;f`HoSKzm2pT!;GUBEwI?^DxSt+k3);`=sITw(lOE@Hd9rv$@)8cSn(gUvv8eH7;iyaU4GP1cE*9I5e>p0S>oOBC4=2J!6u68}ROfEKh zKDYQ%7M~cV&{1->k3ASg)V}%RK^p_}9$R59+Cs!lHB=aY>@{M<_6bBR0|x85czzB> zxhau~eX-vK!+WoRd5q*?JMrss_}lo?^TcMT`5JeY7lpu&}n#vumZyJ;;9)}&S1 z7&iN4A4FmOQuRDMb)D;s48=kEm();7{};nXYZ073k+Xh{TEY3U1ASrMyUWxtG?B=9 z!Q&ZGtv@aUz_W!VfOTI8D})*{M}Mq;F8>(K&+`m%jaYI%c2+h#`iwB}LDk`NmlK#> z2SR9yPB?;uJp*Ob>9>!?bLPIGntnID*HPhL`NSg=_~^_M@z4nn4Fs5$9og|Aej3K@ zhTOeOo}@jXH#wu1mQC_*f(=-M-|x}CttbyzL!?Q08jxx7fQGvMkA}*K1)Rx85Mfiv zpt1hI0+v4$R7ykOh939%ee=Zl=GEvns6LQ#zouW_BaxzRujY;YPM@I1UP(->NxA)< z8>$oruyyvZisc{6XHY`V&3AtgTG7W+stEM|@-kzqOCuR3i*DK?%&_>OKQuw z|IzGwl`ksgy3<9{zx%>e8kqX_OKZTca5F<*~T7p@W#*x3@h|NnZ#oRHmseBRVMIiI;;0mguWg6zW*ll{&0Ita$$DhS%OQpHjEes$PzN*tWy*( zF4xyonq;O$ysr76>OM8|ojm%pkhO!?hBaz`c+H5&?XmAw2)k2o&!3%v8+0-?86}A! zK6QG)?|9!t?AI%^ciJLPej@f~-$pIwBpH{32!x`FzbsI}1=%JDi2VhWjTPbPW^byu z()~H@$TV!}y*;DYZkLH&&(y9?X%HXe8Byzaey$B+cVoR0ZAab*+Odg}*vrSQej@LC zZ>eUv8A5esF)_gAkGG<(qs1eO0q2j)SH5%_bG4= z8CM(KaD2I6%_;WoH!6>QbLnTq&}-y_L(xjRw#BBcp{VZEJ6da3;Fgk`g-i`DTn3d! zS5ky5%fyd+wHbFuD%#%FV(cCsiZk}lejnJh92#!gYJD}h)gtdVc+jNGTY|^A@fu)l zwYK@6Rp!D%I1i7pZs+$Ouiq8E=%9{Gq*&!#cd_j+r@`?V-TK9M1J}A$K;tE(tbwo%=Xl!p$BR*M^=YemW9ZojrwgKch4xk`nnp} z1Hl)`JK;TbI)7Qpb{m!Rpk2kJE)>~lt;w{sC zECd=3%4w(?j*n`WaNDDpi7?)w%oofy03%~8(>%V6x=&Jg9jrB@mMZ&QeeV49KV`SK zF_()rK;bchWU<&WUp#-+Pl%qHx0L_Xw@pGZfE`W%@eB?(edb+LmqPaaoMXb6QJ02L zq-aw;+?~GBEjCu)0!4o-@+94Hoz5)3z;MnlZY<^R#J8ulYZpeyir-14DLkk$FEHBZ zoZ5XPFtLtixZ0(3T<_*P!^U+ySRan=tB+yYUR|%|t`~2`Jj}6?&+77i&_HK|oNOWW zEf*ngr#a_Nb{eHsI~8a`H`q&|eY)FXRrZmJ=RJb7%}^c-_)sobMm7ZXeA8 zEaPVT?uTb1DwQ%= zxs7&JwR2vL9bFM|78GksSsn9OeP;|E7j~eQ>}S4X>auUOF($OL^bY^Q)aR9gKeX(M z@7>wanY!7ps}6aCOuZGnAHR3vA${8zhdO~WKie3X)uL>i>03q>+}rTVrlJTpQ#~00 zw(R5W&1IZ-;{~sk{>Kxtn@c!VM!odLBolUcnq&c1{AiBdYid$a=PQ%rfQ>zQ3RD3l zaB01$`A9{r?(XL96i@%AL2V^NsP&`$Um_Zk@_x43|6`3^nx^8sI>t$~Le13kY^wHu@(sO#OXk#XQeFH5dh;jW+FL=SyFpPs$_i?h2Qb3Jz{czVgA z>5U^lzilqEcB{YjG40UtL2;l-$JV-X-|~;fb2T%h+l4x~mS7Bd>QQ3}CYAx!#lCw% zV0$Xr$Lr1-s{yS_)lSe@Dojxqa0+nO#(sklTvTmMmmv?2OL^iFZASIR*j(4~ z3_GDX7slB`)l3 zS5zAZ%0U{j25dj}#~fuFh9>(Qq*wEvkr9%NA6)8jL>k{1Q57+GO_!Nex0>Kw)zo=k zJphxok9RbG1(o>iJQ{S1JMvmQ9PH-{z~xejt$2wCV62SAg64d{)dFuR8kfa9hYhV*;iAjw^~zV7r$EXWhUhv&i5k&3YKo$n(S6OnpTT}v)bo?nE~OwDrBAU! zTb-;j?-ioG)X@t^-(%V_TGl_77*Kke_AW{ho3p3N|saUUC<09yQagJyZbmK zW23)5ReCb!_uRD|y-ZqmrGDAqykS!XUHh~ldkE+3o;EZDqedZFjTOsLYF+g0(&bHc zP6#9F5)X4$czE`5jop5-(sm!k;yH(KL%VY!i-`|KcDpo;Y1u=^tM|w=j0yRS2Ru5G zDCU9JV%dxKUH9ec8EQGH*w@8N2Da$RM6ZSr)vsX!u;{yYQaM2(DH|H5>u*=5d8@W+ zB}`|X%G9es#{-ov+AU_UD?i>BA6Va=%xAQ|LzAw8v8+Sgr>F~g7CGFjs7_5&wnC?I zu8C}v3X^s1u)89uYG?V!*u@NZ1Ax4eEZbvxqMm$ zz!65ZZOTJ_onE(akyq`9N-+$(b3c)-0>DRmmZDf z#=_d=n)d0ijoGfxIGw}YnRKjEr+B}8!=bBNkpLw7f;@$g47`S5J8nrSn&DG*!>5j? zvhNFDeY96**6$oQv{1(_s{%o8^L#XHZtVNr_@eFJ3Ty;Fy4O9*RC#TDNZ?2}JN-D> zGVdtvR%R!A$Zd_|61RIx{eB$QSZ}u@6!mF-W*ApI$z~BXgsRDq|oCZ z*k5LB9L%}Q6UJ4yL>4P05aDvHs^dRPTk_j&VR=+1E6Xitc7$gl?Hy5$PSNx zGe-y6hvOv{Fq@5CY%4gfc00|%;mM3yB?E!+5rGMT8+mU%0D(>f4SS8KfgpYQ@ZU?S zCeEZ>e>7)37MXy-7Dlgm~l zr!G~von2IH6{F|IC83EFYgZG);xLS_%f`UMZGK$4c{!KnKukj9Ocg1F62=&y4OMUv zvSmy(R=DG}WoJ8Bp5wAO&?PHs+Wzw5ExU=vP+hFO(ZX=`dqKN_>(&Xd=+X$v#Q=mg z08jCW4ZnvB$TD2&`0UbO?&8VFWOGzEFvW9C5pCob#a(=jmoje<7EusebOBK-W&{>( zN0TD~AO#G7SxS9F#t{GYM`^37XXAF%X>(;KMU9Jm?IXj50uC(svw8d;O)h&`>0YOa!`BywApx$UR2xh{l^WRLL93+l3Napz!b#Tzv4I&)T*zP67q?EmG%E`#qod% zR&|iIY31H4t9F`6_T|^M9;zs)v>zF2Yio;524;(Qvtoec00D-kXnk(pU*p3eT6wCK zgv1(w)yuEfDxt_5DAh+SCM>8F%PJM11dL7Av?VekU7azxO5F`)? zec&+bN0Xc&g0rA(R}jqJt5S?YcCWy`;JbXhHoe|1fR%cFdhm^i)3E&7aR=6~IfBuW zhVx!20DE_4R2&)(xB4=qPBBV&yLasQ{(5aW^!dD`o!{Z6UdO)7A9+w%1cHq?YlS^E z%%znhsjBWJs7K%YAH>4QV{+ZU5)v>Tc4$cC?Vh&%m4;(`oabhFNyCxo24w2scPG}s z0oQBM@#f+OqdB8XpaS8u^}y?BcV_8me?_h26V#0rQ63x{2400QY6W(L#Ld>_gbe#tjH6y$jdnBh}Xe1@Ujd> z<|f{2LA_{$e$cUP?fkpKuYT%&+aqqSKNPtJQ{6_b3yW*s6&mA{i*Z}}V)OZL7o%m) zJ#9$2>rwuGyu6+NJs)PkR79@@V_RJP^x#tLMXX*iHd3Hj_fyGb0JX0&^_J?s=E zX#Jzn=r_p1tConUy?+1t_P*GtHn~c9qhJSuOi%ZmFW8#POV@9XWEx4DEo){ruwIXn zJhIX=@u~D#{YWZg{eyj>r{E6*5Lr8x$j^M&y%e`MU)XWfttL4laeUx}mColQ=L!31 z=)Km&KcC~NQ|^*vZL#x7)`Wi3(Vu05Ye$9Ew zlRpVd&X1($)4#tmiW|B09`w@ga*8;XuTv9jdeP?yY=mc-){ApY!_fP2-@8@CzQv!< zQgk`o+vI)S!;ZK;H|B~*jhr%yv+y%1X5KRK*-WT(7#mf2xT)R`{pEcPyYn5k$9}{0`SPxf~0pxA0>fK{5e^Y)TT%Klu$#vjkq}cME z?LMO$8eNTI56-nD8raVj>uAeedyL(4oQx0_y}{O1+RB-7Cx*SlsK)t*%3{OrFUDE` zPmK=u4vpMKruV!C9=#9=*!p^_vnoM+Qw#q=`@qkkvRAjS*howd>t0;;<&rAVZc|Cg zX;39moZL#&he|H zJ=&REex2v|fta+4gdH_vgU6r^U|#!6*pX-P^PwQG^>$Ac0xmU&fgBd+#>isO!SDqjQx z`orp&kMsj1_h#RY@WiumwknnL-7gr5$sYylX|O^wSD20J-a? z-sPyf>n(x5=10NGXf$dcPOyXK5}Oni59&>#!d*#i%H4V{-f_!$_xqBTu!{aE9ZQWqP6 z+zs%i%%7vZ#@A>iCEuwc>+d%FRNgX?K5he-J(Jy#pF1-kvA;MO7_|JL+&nZq%+O;x zp|8R3IKJljfAa{uzk!(ODesW)Tz6iSL95<+n#nsFcsM0MXd2Ugu~ET&%epAa;`l_# z?&ZbK9Rr7?h;R02Ei3EDZd$ho%*W;b4zG9TAnFvExs0vk!4%c4xACuM)ocb`tK~4( z*0YOA9eO5Y_T}Z}!v`9K@<6~ls0}sTv(LV2JlMWVxsxklecjQmG>qP*qLj|yAdaPS zCo3!HA{njhs_=+&cXqg}FB6PXjvH*$3J_U7M1_z1d0W);b-fW1t*-OaS}&QzFzX`> z1iZ^;wGho&cP5~j9(AmdZy^8c9XE9bS}g3ELcG;YUa<-eg$Wr9H}%_)5TrtR>>mcD z-dGk|@y@TA$*qd+{XiBhgpHvU8nTx;Oh5fNe^yyNZVQBL|Fku`84X^poZT5kuC6Ud z#Y%_b{q72xj^;MaL}?8D+WK(n{3}PTMg!T+IdWY*%v9OsP`m!y8Q!9wdvkccr}Og9 zO&2t#kQ2cK?g`F9ut2%g7~hcQw8OXFEzLvK&QkNv8?qic8z$3D)?!Iv^b_p*u7i)0 z0!L*0r#>%K0GTQK74;s-)Z+V2*Vf_IFyxEaLLCqkZQI|G_j%`iI|5Dem`B5$x!W?c z=n6nWQmyb@H!U8?N0qggAe?#d0wC)yS%qXl+r%CW5=cat*67;#WpBijuQePWmb5if zTkn+@%!$L>PHALIx6Xw90*>TeKI>Vg(KsH)TdXjYu1ylcCO5>B@z(z^OF&iDYJx-| zYdg%!>lqt-y>LsYnO;_smPA9+Cvi_&*ZjpFmMt3kg9gOdVNGsmK@haD--Nt035Siea>9qAq&IX%p}=rCy&14}8A$jmIIAlQ^qWP%-J@ zs^0vj7>enKlFwMZ&xrEYK^96@KUA-{DVkO0-f`b46Dmp$e_PwABO{|Op{uEVv<+ui z8Y6f1DjjpVTWm{ky1Ox1h#}LQsikCSh+o`*lJgsvz6Yw`XBEs)=hZu858BHQyfmrc zLlV>Jnsl{%N-@~7T#ebRG_SyzuW*dk1R$OzNp@NoAn3f zuMwvDPA0}#ygA;4VM-)d_9}{^Z!W;I{tE+CVYzwM*XI)6!l`z$8Ho+M9O}_`P#DBO5G%9kswDVZ;aZw4USUj zye&Gz)*v>Eh?;|d_#0&kHTG^sQ+8ok#1|99GPSBJL`$~b@eZ-&zV&pd-?d;c)!UG! znI*7W$1PW)s`&0v3$+cZBV|ITMKxr(Y&odmqH9Xc6Fhp-F=S+Cd*2H1F_9H4X-e3p zF6M+h3rx?F);AIZ`+Vv}zy=s`#jxvAM}#q7eQkmt-8j9eP13SZ(y$zuh6!o4To1@% zU4N~5*sd)0Y=m+Hn$0{2+hVgz)7l3FiFpU6e#R1?QmJ_v?LP|^u&BOG$vog?1)C$SNyv;H%sR0G`^lF#!iuzi3>>vj> zyU+ofR36IGS2fs?JAQ1OOF5SZ(Wrd)uwE%}w#aMumJTsniu0QiO#57jlFv9#R`w|< zZP|~$Qw<78MJsMV0<0HeKFnDepS)KT^!!_dnDVpuN`3jWeDlyB^x3<+>huGBS|J51s?sur`qXmTqj>AE-XsFhm!ZLN!+LQ4On zd#R`WMYHQnw4n&1a5nknf^6W>xg7Ri(((5e)u&`a$3|-1+*Xzqa;!L(K;HMnAe;88 z!o~!TEPtciip*ReIt9O+pxWiiFPSH&kp#z|iLK=7;-=!#;38%)VE6*dQf!#-4zAy|Xl;^f-~$!8 z*6Fh)59>5fGp>_U{n6d?sUbXs!s->gXB*mBy6A7=U^k9nraJ`a2Rj9z@`bu+7RK9| zy4g7GaLip&I{2!RQK{@>LsmrAL@V{e3rzl1tgKhMi)m79XQ9|uRpaV+aBu)_4p17a zlft=gQvN{tezqjz8K1()qk4K`YKxVJjNs)6@XIs*bz$c_3Kzn%n%k)E>2khgNpU=S z$a6R+=05mzCr6VK4vU7HF%ieU(tW)Rf0s{YUensrIweK3WKn>z@!ONdC5==Dpr5S4=ViZ zx3Fx4KIAC>bXfP~pDZ92%>Q*Y9Wf{m-hB3FB9lS3C*zF)`uEna=s#Ui1rm|HCa?~7 zLyoIXG!UTnPHCmyAk@5S@qvn1(;Jdq^ES)m&ZFtGax&jQf6|`^Z;X6JICJrrSm0IySpDm8qB8ONSG`+{_2=_+FS$2z zqERcA2HH37_zFt;LYPcm*mwM57n=?9<@b2}OYs&|Ar~4q7Dd#`WMFfm#iwNgQDMQ~ z-W#}A9z(O3bd`KD^Ruqw>B!Ww@2Z}5Z<@lQWtcCZQi^*TRR^-rwQ{CrKNg!3j&+&R zU#1$!KABCnY)I31;9!9hCVx-#dJ?tLus+?eIGj3UK;uyONQH!0lO}URQret`ZcGT63%iU(Om3lJ>eJ==DwCe_FF{EtyD$NuH29-wm0U1LsElS5d=9gIs9l6 z6=nPlwcneZ(}N-qK0j(;#If{3_9us5kZ}a(HT;tHKkMsOu9AvtLb5;8eE33Gp%K&0pKn!dW1et94m(DIs4bXPoO+J zJa*tX20@ds?UbnmPGX>47W#B`brXA_z+|JXK}2gn+t=``9=8^ z=rBQK&r)5uW$_Z!18>tZ73vfwx~zg=g}ZovlvxljE3G6*ilmBmN;~e} z+q92yT$YavmiuHw1rN1O^5A6^{1R4Zps3g!dPWP-yv{98|I%%NF&}2T-pV}gQ{v?l zQ@pCd3W?HoZOK24He`#b6oupW^;Wn?u8?PQg)UGl!dh2_1!B5d%}Ue1nW zv(#56E%{yZ9`ABUoJTeY^ltkH>IA|O)RK&BG{mokP|0)0g+=9I3)Tk>Z&$6fr>y#l z9stEfXOS~k*voQAuo7WiL&5SSgB67cbmkCUJ$+4zt7mtLgUWGxUu*Hzx1sC}=4kKL zb8WmVLep;}T9E&*{1fTofY9xqocCTmWOS%|+LtWx`|GVGPyoUJB+tw1LZ7UmD=Ni7 z@US!j9z)dS7OUD+10*f?JT8{Wk|XvGLE+E>fK$IduMhM^MOV;8*VAd{hd0>{lb@^R zj|+C3j=cX$VwDYd3n>5PFWK}SZ;$z5jd7lX0DLle5<3FM36I8(+T612_&W3*> z2VDA%K+%5dHR#M!C;tUO=|Z-`LX!BWGob);gHnFnhNc%NjxGT{B!5^!%(M|EHXX0E z2x_9`BW`2j_}r>JAgB5`&+}MTav)3sy#CroTE01a16{pf_RI$?l-Cv@T^;~(aD@Jv zEQ6^s4`d3g2y8fiO-udG1bLwWwit!m>~rna(bJL0Z`fZJ6PwG9TkH^9snF3g>eZ3- z-g#2HHDtS#|2DE*=A205q5b+)Ye#T9f9+SmLxoS@<-r$M|GLB|>05`Y-~F}hgnlBv zI~BJ)lf0-Pggk(S=zcx=oqjw3=%L3;LzPaFECBl^U=IOp>AKmghbLw6ei+B8Qm&dR zI#^u0Jij-Ztr*v~F!4IabL{8G=S#hMCY8J;bw5}2m&qy#}>|z*++JmFk@0SP*`6o1%EPD6% zj95%215^TNR`_}90ozl(RDHDlDsmM!9?W=;DlUkeVQ9Xv#6BnnJVa+ADDa%QXx9{o9|jfi#7#^+6bQK;n zr{Su1Rs*%Sd#00sV=VJp!Sx$m2`KN9W32vsN>yUloGZGAfvmx8%%|T+jQj2_*DF*V`8~DpiW*2E07n!VaT%GmX5KtMd+lFID4rd^I|v zbV)alE~0cY2*f^2EFf9+;}13Jc?FUVmfvj@d$am(m?(!af!g0(1fbByw|)#xHdv=V(W^&|IIXn<2waE9NkajeA$If8h`16aH#8 zT!kZsNO4+X@aZ3Rh`upxk?jU+Vpw^8$&e#~I~oAKenlBquk8{Mdt1lSPUBGK#ktCh zw=4ozJI}-s-XYo?K=IIZ>nTu(ILBCtj59q6gAN<~7zTD}iivwl+ zTV$8G7EL4MW5 z2@F~rQcDfB7tN}>_-yX~GPx4)n%hxJSN9k4+#l>QKn5sf^M-nZik!BwWHFa{?QJpf z<5=-ibga5ven8n}n)Eq8E9lN&O$X~GKoiKFrI~+7W8+RTnNS;(GL{^!@BaprFZf@J zJ&0+!vr_k)S~b^s;@Om0-v)Oo!G1lQifO!F(dZ5z{$(c7! ziv;GUrFAA#K!>B{kItBuH^3Phk`@={&n;TgBF7^;CeGr!lPSK|NVEKep6?omXer>^ z&wSe`ek9FgCrtrx)epvm@!CQwk`T-c_p!7=X?!$;3H#*=2i;u~9>HmU?pU^u8ND72 zR<+St(6-F)fB`H)=&`IYB%LH^PJN+s-QRy)BQg7k{iB20~~)>*q8!!(QI zTN4;x-_Nz}!KnUFN^GF6m*l-h$b;1r5TX#IAa`uBWrpMYv%9A%@8_aFdNzclEv=Cd z$JSE4BXgPQFsfUrEFTu6{*m0?p5)HutqR*P6$fsk)9fb|B22d79ZSl4O1u}@>D0Xq z@S?3+k_R#sl#ynYzChrQ&-#TcqK?}Mdd5$)*tc+OVeVFfN+zeN*?5aepM}4yup9j0 z%#)PGZ&Ol}MUZT4|4MOiWp(AaztGYMcLj{@JGItPYU;523UB7f6|z2H6%0wy|M&P>JU%tt8)ckZDW-xu!_@Nij+KZ?-%DVo)1wf)1*a;TpLQxvE3L$ zniK^bpQ-wz&50z-=0!LkRMn?AYuD7fZM^CEtI3#$x%2VQO-aetRFd2$7acn74U{ZH z;5D+WY7Gj~10Qenj#YWlEDY3C6UzL45y9CUeD98Kc;u$ua8zxh|HylxB>rRY!KeRl zGJ$(}SDMOoH7g@u^)_zS?=Kf>_|F=1yt0j5{=>;ozX$cQF&_I3EbrAv>NDm0vb;LU zU)%XPpt^csq7V$ypN-Q~?Mu4f(VV?vZzK|e{G{AP7qsYNR~ojMRJtnCYH`@%pvH!r z`4(>N0%7mn*QA4&t8CebrPL#I;`=iVsU*p?8oCE}s4pqK+79=Ad-sy88#f{A{GnYAnw;gnF=r0 zb#H6*3zhF=q1Aki!`G!WDVFlQOkB>=MNm1B<=T(bR8P80hGXDXVyT#>IUMV}RyoQb zXq5p)!0`Ez2p)ij_czb>urhROPE2vW)r$XCh@6-c?xiaAf~`tc@uz zT77)?T@co%x_Fz(;8iu!-mAI@9L&+LuqsR17yT^P$@9K&OjUeu98X%}?apI}f+Idn zpD+3gHDJ^&+WzK@WDs^dGO5HNPls`kpSQofhA!M`?0$g3eM8-TdCh?C1*d)}~Zk zdY%vk`7uDWAcy>Wnj5GTSP}Lo=OU1y05mI&W(!cl2%inObNEc>FER~y20l$e3l)oy z&d3HQft1=toKLt6b#d@{g)2}nSiykrXQD~S+nq+-{vG&7z#|dQ_bNq#lQaP9KNXL# zB2fU;j^of%o|7IC4?Q4A#09!F=+;8_fY5@bg$oZo18qlad5w@`kEYk|j0Z{O^Dno+ zHyK*runPVahd|kY*n#?@o<+_Ty`_O3{oCBwL_Pz3oTm%gbxHI=fv^?F^t7^FS5^}t zNn;c*Of)*&dXu5^#3uv{UV7^6y+08^45I}{H-L5prnVpEZHGsH5_O)ns9B4G~EOK|2o=UymZ3u*;6nIeYJ18*eB$7 zPDhA1ZIBR|34?_i~lmINv>Tb3={Rqau2Plb@t+H4oNKws~Bz>Pz zTrMkBx3UtJ0r#9GcKr7V4JF_|jdcqPpBSP!P!_aEn3`RFI{Aea;n*m|(#wWemwFBA zl7EIJD-DLl9@+N%HIU&U_`5hM;T-esHGrrlm`(u*H9l4S)n`?I?&~Lu${XB2{#!BgT3aGf%udzZH^pK`Ji6~1FB_CtGhZVn)s+f9 zIIh5V60Hg#T9Kb39?T(ypdpRTzsJ6ojxsTdq}0E@-sxFk9bEQiTH&YxaS|P#h@6Q`P!U zNYORW#Y-|a_{;x?PqHMKY0(kS4OfTvzSBvZOnjzeVqD+&B`W>lDfw$>U9af92nM_P z?cxuVCzBfo!#}g~y!8H)$z_!yvxQ%MK)hc1YHIP7q+k8vNp_RJ`04-Z?#jcV`rAGe zg(+h zp7(mL>wW)wujlob>pJJm`7ZbOUOxAIf6kLH;6?+7)@0J$IshSz>R79w-z|({oEz5t zCdUPDDjg>K**=;KncKMvM6bxkt(Aty#P^Zkfl{GPRVq8Yuk5Z|0TT@SL1!`H;4#o* zd_Cv=b+oI^+T1|(NcB8Zi~7E)*2z*NnQFdGeYl4GYXEeoAPtU@&OQxT2K`^QzQnTr zf;iXIWEWH1gVy0<8Edzg4|5|i&r=vSs5;KZrk+B+pJ8?-!M{upYYp<76ASD#(0K+b zJueGMvq=Z1)tRSd8rbKT;Fr@FCJ&TZ1ayukFW13B69Ebt@kQ(jiJ3`&0w@OVSN(hx zm!NvTsx2K4(iRPX8_yvDNb`SAngbYjcc<5badwHOvG zsNi=y$Wab4?EO|K2U{Qezpxz9D1(t4_bIOM_=j~(zF};dviLXa8p^}1KdWuH`S8Ve z_ycD+BInj2b<#}ULyxC%xsNtR`?%spPk<^wQ#I43fSs)kDVw>=a&C9#^b=pLo)+5f zSWXmh^nrve|29e7Yr%Gkue5_SSxrx7eFWd;_jPq_Iq7^{eiOd`(u#=(-&BC*lc8oL z?db+&7-OsW>MiPhKNdYYiBoVNt$hpQr9nkM((^oje%TW6Q7m;h!AS-kVxsB}WPH)- zZn(*i9Aqyg-s~d{<2B!TdxA6)WpJLvo4Nj@?vNsi1LH-zpv+#?b@Z2#-w{~@nT+j@ z2fxLVDP}d$SP^@mIaT`d-AHrnfL7Z}^D$rj@B*Oi_+6_K!aHR!r4S|Lym{3XFe~Y# zQfvl-?8#vH)f(jbEwTO6O$JK?+Qq)@Tt*V1`;6(%Vs;YFl|Tv7YSE{W6uj7A+?RbH z7cO86t@G2=*}QG+@0B8xeD$>B4=3FzGzsN5=Ss5`JA>$GuAYMBof~yxChg3ALhMmA z*i zDEIn-bjp!<<}duAu{6J1Rgo$SCohv&%O*3RMA#2yeta@4&tzp4a15?HQ)Dh2#%t$M zXuh@iM$u6t;f6KJ7zswZWi8{BQI9-ZxiN5Z2G1iloFpBb1!>C9x(;S_$bPH zi{*^Yh=lB{u%=bTaKfLa8$60|MCa~c2YKg$YzvO0sB@te@3u(0?CW(_CG7Hu8h29D z<;{R{i=DoRu2Yc!I#O~{y4gSIgjwO$v=X1y@OjRC^>-*p^V$=tzPpnaRdr7;?O!HY z=!{||3p3K_wH9f`oq7#JW_GSAOD<%j^%MaAPA{O|z_li9_4L&&!|9`Iw*y3f%i|dG zxMtDxZN#r(%<84qjQeYmjfB=BwD+gIw8V$7X?FYPqD@q@xZA^6941pnsn|UFPt%dK zT8HOcX%%|D!HGeWUH4SJ#E=P6_>7PU?jjyTThgxTRFP5K^oJi6?fU6zZiU$GUSlP3 z_T$k@>fq?8&}xdPv-^+|+bWkJMQE3Me)!`}D_)hR%yXi@1ogprXtTSv#0O(A?zLtm z4G&`3OH=Nh%2SZgPQl2{I0x*jr2u)R3PoVXX8WZ`n+m1y(`0!sCE@l}sH^?Gc_6#B zTcMJqEeM0Mj9kg_0Mlc*Kbjgf^yX1yf-rs? z9?}|GL2LMMtW?hqi_S0w#keYrl&1TUqhs%mxeZ`p5&cQ4{z6oLR--FkI1ChJsD0zT zOcI1i;7;&*_5B;?XwTF-Vh2DMqTA-`aVdY}G>dKDxzBW%H+!3td~HgM zU(99#=jGD7532{O!vkEO2idit&sq~wByQfLpxqwQS5chc%4*=H1O!|PtYFz)ihzU2 z1oDLuf$8^<(Ga0!)bm+B(rPh-8w~|+1J5pz8a&OnTFXjO)nedElc2P;ALeD~)$21elst*rVz`}Es zWRapEno8fN{=a>7ick|T1}ko87n+oo43t>#%Q0Wo)@KwDwXy}3u4qvDihY7{!DiRo zWc3N|lLR=Di0mRsKdUA}GLXNBEi8A#*hc!yeERG>AoQU@#X0I^D1hHU(-F1QmPzGz zrS2H>UFSJCERRnmkQ8AHa6`hIiTayE-!1(7UIzo@((63A(67sG>2FpJ2v9f%y`f%t zDCY&@OkSDFKXUHa;wGQP(a0H%DS=ZK;b^-!E~R%M*~~T@#5ZT|NZG-Hq7#;N_+&#` z@Eqy};`>8;Z3=0h{eHbF8ZbJ;tQF!7lBCzVTYF#(Y|`$aeu8{g+~ag!E+3`}fPF8u zVJO8LT}&54C{)^7NzzsXNoqVj`6Jn8gV*HbkG}R(B^qq}m7TSJojd^yaK#2_|ED&u zzk~pV{ZtaM!=P{{&#>*nHt9eC{u`OY0Dp^MFE5}6)kRUq zoa#WoB_yv@x*=4Ww{HIS14h>2nvf&{l=Zw2ADrdimrZbywjI3p{;gBSl2ENrIk%1< zNub|RgSMldSSrn_?n~ACuWVP`CY8%T-97e-Y*8WhJ!OW@NKiubz_)0Z&}!C0;GB22 zu;bdq*M;M2Ke8B=2wPouue|vYEsEKGXBoJ5HUnd=9Jh9h9E<=ImH)_+&^yNN`Q=`M{7 z0dBFXi}6a$J8Vihj(4qPz-GsW>>j1|HCa|m_%q!tJ-%xnz~LEqV+~@=lztZ6Qs+H) zWusnMnigHp6)69T+B^&Dr0UKANW#G)<#5e+MyS^Tri@+;y0>YwE&aT+ef?r>T(#3c zpV*zH=e-MMjJ-z{=#GL-sJMt2f~guWdd*N12Kh+7aJ|AlOSbJ?if^T^2fMj^A5cI|oe7u@*IP)b=X_=0 zao^VsADa}9J~o7(D9Mlx4%JB)Zw>mhVN$<17?4uQy&B(wq>6||icoee$a+B(qlB#v z6a4B*YIOXOBUTO>ZdK1VvS$4NA~P`E+}X}~GsCs4q;P#pxTKn#NT{@En7-25M-#CA zTo1qO;!!E(mcxWf5~Ch!K8(6D?3%hm7|Ihw zzq`2h1jDDof{xiNl5}10) zhJiD8qZ;t1$S#4L2IV!>Z&1Gs(!jd>;=b0v&|&f(FYKY!{5J`HZulFANNTHkP*w!A6Y zuv#}jAb4YlKNtr~K8lO^f6ivduhl4(HTsQr>T=yNJgr8Z*m?Ek@O-br0v`>7?4fvt zj{~L5pNSszZBy#gL~>nM`~Q`mN0+v>US$#JgSL}ow1kN3HS!LlNlq09*rK1 z{5rG`Q_oStS~b2so}ktHFq5&=p!6M84pY_dWsE=hEw@N|>~1DAgPJp<-r7bs(U!8~Pju44=!cSWf1mjgop2F|akW;ngs1MoLK{uuwVa0w>rTqi zZ$W_5of$<&-2;66r>0`#x2<+r2GSGAs2}>-QEFCXxro=iQ@cgWU+rvvc~NSw!4^UPmQ!N^O+~;FhX-&4`WL%Tr}J$%T&? z5N9(=twrVi6NuONJ#fTNnCN`gXQ&4*8fK^bJ<1Cu3CmZT{2gkOlaq%5CGRha_5i~C zk{$kjmill4X3O}h-^NMceS{)KYOJ+F&O3kkRMPY(SmZ0?qK2L!xm#2W?o5+a)Eq$YFtg&5Ta z)?7pt+6l|KY@J;S&+gRD-FqkEpM$~)HCqKW>ncq5b(w8#k8MWs!3v8GSqlDR&IP0PBQ3i|fS_*uSA;`4|xi zbZ=S*t?ENVb-p< z!Ft-F>GGc|!0N41P5S>e7*pEl=5SPgL|1;!+94Z*bKY(AHPa0d_;F%#11^jV%*l~* zub(nlQaEKq+0pE0X_Z#iG$s{L`&2yHPeXAL?Nb05^Vw}?qsBr*N=>P+$=5kRc+II1 z!t_TpClRJ9%S|^LRD9~!On-9&pV9X5ZdN8}++lmQ{(u;Z51Kq&g4kaqq?L8@j@tWJjF^W}6-T>VITVL-m;-Mfgc zv!4Y`xln{;?FbWnGpk00QMdU!yA!?mPmDbRWvPywd#u)3hd|G1V9C6wT(1I`u6!sD zCmR_V6$<)Bz`RPvPc;ihYB(D0}pEdg`cZ$=CUsq6iGIL$&_oHsJ;Y-54 z#qyC|tcb87%y@~^Om1c2tuLqxzC*$7Q5aU@ttG>g9MP|HlN=Q{ez+7)Q2(WNsm^EC zUbJy1{@5y?;M)8^C6}Uq>r34X8_eA|Tvr{s&w+*}2gAjN;8zx?m5PaQh#l9T!n2W5 z8^)`z#f$W|DCoVP4XSi6^0bJ0H6D_xg6Ae}trh_uU<9&{C|uyr3gcyG(tZw{I9nw~ za=}l3^!!v1UI9h*J_d#1RB)pTEG_oy&*?1vyQ+)pO53&E z7SZhm)=XE4AIZ{)E(w0!Gs?+mv)10>pV;|VMHcX7U|E2Ca+xI$L?wCV- z&54cFbz1oD3!CyGN1uvte&=*)7w4FU$qg|}mDjrr<=&eup!47@hN2HOW=}Lt>fnDZ z&lR>=@nXHJbZCNSgbfo+EIty+-uZ72|7|d?mh06Ib~_s-); zIbjNZfIG@a@PJne5A4sMgSdLY7XTsa8`f7S^1P-+j0;ozPV!R1w5T9VoYa4z9se1% zZDxp_4r`=toRn9-PePTsxj#Yu-KKp9ffMeZ6((iZF1Pi-;G~%C0~E8B3Q>XL!Rrd}Zf@dpf< zuSFFB#i^6D@8wD1rx*ZInJU-IIACwM!PXx$aVDj%k#9lwfDIcEr~QoC!8<&mPlQNR z83N)qKz|{4eaHp`L{OR>u?1qt@9Xp|Du{9L-v*y<0BM>G0CN=NcIY<(G<@J;9s!Yq zZ+QTTq5$FJD0%2BC>@4tUZvZY!YfE&sPn`EI2kQk@( zSCcRe1RT^ayuSrx_y@JUMo88F=4%{XpnJwlpAiieIsncT=epQ~?ijcL1fDQ>$>1W9 z$0!CJt;Tysa`mTRdAGq=Lq7eFUk%|w6xc>Gr<(zY9zjZkgMkTw{bD-`I4||Ado6W} z$DJkTBOg;n3Ay*xMh_HPCE%~cN*t{7Nl*cD2ozk+43vku4OQl>Ii;S6LBe>eh29)| zxMDv94?zM9AFh6Qe-}VZ4xQrv$Pd2!|M*(YmXbX0!f5e0fj|cSwA6G}OH{1G{tG?I BRq+4- literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_011.png b/docs/screenshots/venus-os_011.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe5874ace542aada595c60aee67eb8827c036d1 GIT binary patch literal 30431 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_012.png b/docs/screenshots/venus-os_012.png new file mode 100644 index 0000000000000000000000000000000000000000..329e4d32dfafb5c1de7850bb0049b83165f18e72 GIT binary patch literal 30695 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 Tv<8Or-jNcM7cF{Y;P*cOQ0Qy^ literal 0 HcmV?d00001 diff --git a/docs/screenshots/venus-os_013.png b/docs/screenshots/venus-os_013.png new file mode 100644 index 0000000000000000000000000000000000000000..49d5f1362fb49aa42e240672140011681db9db84 GIT binary patch literal 32555 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

  • _8E3Vi2Y?D{Qt1=jisk!n>*}i3eQ0i#s3I^ctw< zvon`mBm0?^AN(B-v^*_f;8~~CyPc1*6P{y}q0efvwBypYCADe(PX@-!4<(L+u=xo{ zX5hs0!WFx9+1j0qIuOa5hc<(}q2>fc6w}&@_QD)-#$))5o37xBMv8-O^c$sDNV?=- zt{xN+kD|uvI*GO;V_N=lrNA?ox}1I~HTEaggMeZ{-k9^dxh9DwYTbV$n3&}`mTz~g zY@wc_;-s*?D$4L8P@k~0GdilKuIBV^i+jIljw!@7nA;n<@y~tZWnMK48tzvqOS6tK zuH7>84jK1hrE0;AyB}OGt6jbyknzP+Tb;Bsv40ysJp<+g4(mpqY7brKq=NGZi$u1| zL8lAV|8g~uqh8F9*5*dac1ek5X{^PRuQfCpACOzgH#9c@;Zszi2wz zsHUp?r1A5)<_PI!02061{BBZ5>amrXv36;=(@hKFkpY{$J1OP*h!4nHF~XL&4*!2# zye4u5FOPwj3fz=(rd4HzNi(5_jBPPP8sEh99!oU-x_^w-&oE@2j-W_#@L`wYJ}j#3 zTm9%5Q;Z6$KX^+aHG1E6v7u*hPkKsz9Rw956@pWhgBRqSg=-Nxmw_zuZTcGeQgONS zsV~ByuFb}3*UPe-#@L^Y5c&2e=k4pSA2T@()SL9<2s1dPfYUy&urVEOFD~+0=NhrB zLK02g$$lzJg8)3c`}JS0Yv^O3>6BH12CibeW1b;h0gIJ6)lhU7YRsJgvIdO{mcr|! zWEz~$fzkIAw*bir_4J^PqtX{cxUS!pe(5#Z4ILAu$hN2G4s61PsHWO0F?FJ+8!R1* zTPysl`>IoWdLg05nDKLVhnm7XhvDN64&xEtRfO3s02{_3PhPafI!M8GH;5o;ANXVKsH| zK5(D(I?H}z3)wo;crKjpVx%VHa0noO|14o&J22dqYBzs6A9AhoPIl+n8*PHiRVRPQ zyL@@3rgUOmvqJ&5)-@$ey790hu$6-I12_9#aB+doNhatkS%Mg}s#2Iis8L0@jOnjdOs9oZtpAexuuz&Hntn$YvuQ<%!oR~?=+uO^FLxxM0+yYL{s z<-;R^fE)nab#}slsDepjmrr0Fr&`-!%=b)zB>yP~0f8fTT@s^oj{;NI{s0lwM-_pc z%#%pY1^lVb`Nr?}UG)A;k-YLJCWztVHff*}@vncXQ)h7|u@E+v29NSfgfEgdrE{K5Z=#9bn(^_3x$msKpi7M{mg=fzeU;SlT z3-5mu@c<@)_=y+zpCE}d7_j_8rd&c-08r;TW%Bsf^n_Yoru3adeLquHkDiloQKcut zxLf_is>pG;8z3WY<4_|TVeYvw9hZ9cJt*yT!!h4tM}zF>nA*M&|f~P zjdk6)TERJ|4%d@sULr!|sKbfOTx$Syq%iH|TE7&i=V71*vyvm5z*2n9L>|6iG&(=s z`FXrq=<}5{bPry$0Fb3dA4hQkBWgRG&ZY$r16d;bl`Ns+3lU_wwN%Y~bo3>#8IFJM z_MN(89Z<;7bt|e3r(w|!g~WkI2kv)w-Ugg0d4IXt%6p7S_f<4r@~Ee@Esj-MZS4Ay zj#KL6C?FbLc9l8f1a(gII~f{c)tdY8bD}NTO}zQ-l*K(R>Y*)~HL~3vaScFHp|s00 zldulbtk`_E=X9G|k1=gB|IC`uk+f^b_~)|QSYFsGgw`_d0OO5psn|tAKj2e(m2;4F zYsBw-T`dqs^{jIDN$a^05i@1M4OQfIt~-+)tk`zhGEaRyPyzkN9@(z-SyRe3y6aar z;Y4o9N}rtp8K3jM3S=UV6wb8`PAUQCsR_{O9Ebq;vJV~wxjgk)ST2luWxk?ua+J2D7R(4LJs+PgbhtY9F}xTLw;~$qs&IR{48xws4N)nOESq zTG*A+jH-7tl`f9-sk+C)$Ad8As)VsQ^RnfanFAWKiELio&Ig>%;=`vW!umKH>;z{NDTt77Ac#vsO##1|2nQ!Uy?=6FOq3qr z(Fi4VWpy6Zdfo32{etYVc(rd4w>2oPRyjSX)b%U$7xg~z0^`c$gqfSyaBTdaJE<28 zNe3ow&!z_@0&a*yz>J+~C~tSt>)(sRXm(Z{aF!bP!c&KrLi9C~H9&(odF;Eau!IHc z`&l%a!ekvwh_$JBg%IKAMK4=wsLih;EpL5Ew09`oJsd|s#-UH*WDrCt@eD$?du@orz|tFJH`t#C5G9t0tT>WE@&JUWc})Ba4+AkXu?WUp73%t| zRl4q*nTwYi(tKo*?KI%Ms>?eu+-N=YH5E2-3j6G zviB{d#M++TRaY^Les#fs@?oE8yHJ5X$a-;ZuT_vTZxw)A1uf9NJ8*u+SA840QgZM@ zFtU1q`j<-*6Vo4XP0Yq&qAcKw9fkaXS>qEaU5PC~HF@y5G{zRq>&m)I(e4htznni+9UY-HWzF~JP zbm|MlnIGY!4jShYT>W6IRVi6C+0|nh#m>UpLZ_!tW5Yc#Uifh;{zPc`Z5*B5kWlr= zO!xS=A3<7OM-(laxMp8SjAA-BD&za*Oe_%;@2XyHZ?g5GhSSbeqo4a1 z3tL1px>$bK@K;u$Z98OAnB57nXM1qcPbZpDZ+eE@JD@M#R1y~FH~E2g$Vhx{A(8v_ zt>gOVAIb6Yb14XqK56@8Fu5(vwR~Bf@7Gb`{95p?@)EDaPi)3FLNS+CqDsGb(}#nJ z>W{2V@A1kqO4|W#${_R5B)CjV%wTCCAkV!!@A!;ix1<;J#FXTv!EUjCn*RxZcZWRo zMfvbU>KhFj42u+Mg{miTAl^AX_&&9dk5yY;Q@ORSqZVKZQqq7R6@9?FLChR84H|z6 zBN?qeHRkEAZ+JA^+^ml1_TY`C9}ZQJ8ZZL|lkk*h@DK>uXrVel>1x622!1W0Ye83w@$rIpZGQx?t}*^mX5j=G zI**$_{VHBq*%;24@k^LaOGXGWuGfTczGq|Do3H=}`HxocjmnT>j%1kYY`qE5uNX^Q zn0*dpoHfb)Xvmj7ngO6PO^%(MY?+d$IS)Q?S-}Dx$m8m?bt)~~LRe2WX1!Hx|3z_o zx<$<(bj&jN@U}wQaFzFW9eoB=uZfL9ob-^d7_YA`2yI(iryY1gB+lP?FwRDIvE_RR zoe<&cfW0Uw$)PXXk3BasVuk zoTJwjVB=zU->Q&hsJ4~OYJGp{*OtF=m>TM{DmG#2EP!E{QcL2ztDNi>5z5uo=}N*R z``(*vx%ylN#?dbRj@U;kjv=&$5_=$IM-Pz2kXF>gw}MeOyd#5-ckG_yQZIuJocftR z0(v_F7lC*55XVTXV%NDUuzVorUN5M;kFLJ*cp*vNs6Ok27NAHz0&N zxgr(s8@&Y~E^_zE$3IlrpEW5=#K=8x9#9Lf3;r#kxJ7est0gTUR1z;He z-Cl@(&nPP!ix^naE?$EVX_}YIrIc}c-bprye`y&b#iw` zv5D@o^YHU<>uonh^e3{DDX97k%K07Bo#2r~Ny<;)&Kb9~YhlAWb(JPJ=R=}v#%>6p7 z0&3eGMNE|M$s+tVeZ0a5xt}@vG|pmjWe0(;ya9)e97InnEr4b=XvDL_<;ntV{}5&T z;jSaO7M^_JVUHrlQH&y3ide&&{@JA_0*figC`f##FLe3FYAh-_r+%1O3Dp3z=mDG? z^>{H=&+`+Hhl4=t^na}tW(r8ds5aFboOSa(G=#AV?I}Vght~%C$Yx^JtO@(zFU90A z-?7y?{oj9>;!)wWqJFXC3LmI!OYoB{ixjc$?vka`-5V;ganjH{VN1yn+2UAm1x=4| zyL=lCNJYDYD&r#|K!3SH2R0K!1sg}CjIw4>oW#S{%5vOvARcCX5J0jQm!AT&A5p5bOihO$y|u}=eBC#D6#Q5G9AA!>Q9h}YbL`xAHm za`j=^Oepo9BP)2+Ws!*AT?L(gVH>w-yyW(@%h}q!YV;(U@qraG3a8;0LkC%#P^!N0 z0SPpuZWfmU{kfCzZ36|Z)U`l0)3g2dPpg12auhJ6&!vjf_3p9Q0|y#!$nsRsg?bO~ zCH6h>4`mO52aXgN_|93U;u;f=Fd_eS;K5Z6H1w5~x+>ZVIsySFkgFDP)4K=uzXse{ zp4mu<>dFfzyvtsctn&XpPlTXIjCv4C0mZ&W_LRU<_m{3fe{%fxpCWq_<+w7a#8xnx zrwG@8Z5yWTjo7k3vTpAbLiPp7`?E-(Ozy++40!0M)sQR11q??y4gw>dP!CC@KR7*# z_|0$s4a2>|@f_`yZryDR8!Yy0g-X?$cAFo?{9j|hrO)Vd)W%asQU5)$jKp#yDit)Q zb>}bcBNniumlEY79KV+=A)R;#49r8d7FHY7-w{+V`R{Fr@JqEHPMLB>zef%Va@vG# z(#FnaII#SPe8}b6HBl%7boxg>t?O!dD_{9Sz`Xi1xCY(dvb-H^bG4gMXcJm9x5QTA zfUY9;ZU)uhbCP&i@Yw(HyX V`_-8~)o+Xl#Kg;of}wxM{ty1X=;QzZ literal 0 HcmV?d00001 diff --git a/docs/screenshots/faq-lfp-curves.jpg b/docs/screenshots/faq-lfp-curves.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8dd6f9ccff4947cf78ee008b5d50d389cbf0d76e GIT binary patch literal 218437 zcmeFZ2~ZPjw=Nt6jEEYU6(ms+Q4w%pP=-WBL}X)}5fGvq6c9065s47e${;csl`ZTI zN>oHtgeapjBtU?OZe$XWpkz`}0n;rI8j^JW_I&rhr@rr;s&l_{?tiP!Ul*&ZAci;n zzH6;#J?mMkQNL4*Fbj9>@Y;dV(9pmfMgL*c2*%ZYpU2h%G}1x z>z1yHjtV~=y3}iz=h9t!JUo}4-?VP^+NEcrkDUrXcVVg9()H`s%~cO#+<&h1U;6sF z$bV^ppTDeb!064<5Ni%b{;f71s@6NH<_N>upUl+Jy zNWNbj!v64JG*c9*x|X;YuEk*2M_rk_VW)8IesEEEc|5D zx#;sTvTb_VGv~1y)?}K=U2hfX!}3$1><+L?`1Ie zdSDFqg~T#D`iq8lcs^8qK{zUo;!cfBWrK*r?!TNQ++O^*_on^A+&i5{V)u-9lGr0M z3aw)A^&Z!my&nAlDd)Gjt2elLeqMdnd6?->VsrWw-#HT+9xKrmRk4h7=m)%Ej>raO zHM*s0Ot)fR5S+wFU5I>>L@BTZO*~GC2ACy0!F`JN;oeoLNL){K++=#zHU|4Qz zN;Gv{HZPzmd*^V{lP+(oe5)+!m^(5zM~%^X-F;eC@qvQOwNdDNbuMK^MMVIG-_)2& zVZ;0lNUKU0+f!*B&RLeaEZg9-=Nd2k7lF-PZwt*=YD}X=9s%6NhON#FTPuti?OV?D zld!I~zq!S$+#(=PLYZRyvJYMLeLNR~4Uu(#0`}+~5-L2E-2i z8L`{>^&YpCDsU3pHIKr3|Df&L4ml-Cu3Ld0^M83Tt1y0^ho!p{|C1VXV#DtTs%r0l zaGdB`?N@7US&{()V~e+s&%b3D$O?b8OCUevKOpO2%cvP#(LK^rD_*4g?rx3+@*a35 z>m-?{A<*wFtDTILpE6D|g0E z)$KoiM*G8)b~C<(Y9lxzapG|^@bkM|0kDYAnFq5(l!XJ{ad&7vVp*C!*_7rKP#t|- z|I5%aaIE*ur6=1gY($UK zm0Adrc|Mx*K+anRZcd#pbkd=V9*k?X6X=R#E+6vC3(VtnSv4uRrVVNgm;wJz{_VQE zQOnd96QzEKoC-e!9f##_;k#09LXGd}3o%o`mgfK&LS63hNP}hs2U&6(WeZwBX(Jxn z@4#KizUaf}m0%@yEi8RF$`?+R0`p-)bN+g;u%gF*Eo05|ys_Qui%xd_UUg>pM&PF< z?TsasQ^D<}RfkTu-6`=Ae_BLsSEaIRt=GKR9u@k%w*KQij=v3#i`XN*K$#jd+HcH^ zQfx=M3A2bAe91x~cvX#Q`UdkrnRr(Od*QJ)VI|TIJvh>Vy0&<>!sX?jVw0sG`~H9@ zL1()Kux{%JlqcRkqzkhp=7dJBj*_g#=$bVai`l8fHIQDm8@4GQ(+`L(ow1p9-V)*H zbY9a8?s8%NkFldK6mGQ}UZy~ah)ytAE{++~g|d*j#}qzv8c0e+Rx!FAN#GtpU?4w} z%a~Y<`n?+XAQXXhb%koUuT9kQx>Z=NR*tErW?4?}N`)w;%Mg>LXbEb?BG-pNtE9KFh2V z&sGZ80u}yU4zJBh{HqSGyme-50F8vTp9IdWly|a?eYTF|T)#*uyS^Qek-qlkXk6Xx zb!8Iy>^t!Wu-mmqWAMh7l{-dMqZl1E=Hj)DBR&1o&)`DEQKXylET@?@I3l990)y@c zprPv`!T?Dg4UdC3X>~Jspg=s8PPC$7`4n@;I@*Zn7t;I*APK;@I)DHVSX_#pdZs3C zFRVHCG=O|eY90e*cBIuA8D1kSN|%qQILKV3m+B5jk2V#gdU&_$ElGrePG!tkAXwC_ z##GrgG2b~g3JqP&pmb3P!#q&;2zcm{VmZug;h6Ozg!eeJ3t2R8DL?5rbcDFJvqYj# zdyIp3jV;|(XX!p@>;2-yW1FBgdtO=m;hnpNUb=hlYu4Id*?&z=?Y~}yPhej;d`@bz zs3*^-mg3;|i}-wYHqv{fzbq}iJknz3Sy;>~+4IEmqxizh&M_ij=AoYU=K0x&<&jno z)?FCR-~4iFhV{9din_>5Hz|Yyuly^AQhEI?5Y_>&7Pbfnggik?ixa#VWc2gYm~zic z^eBZJoFnzUifd$Rx`W>cI9eWsvVdkQvG#ypSY;lgn2of4-_+lDzLU0_V4!-W#)$3^ zssW|Ju!K6+3yw5X%tuLKdE{hPx$h_-Aq?~cLaav8+yGh@8LA%hRrrhmTG8ZGiqSd3 zC18Ql#)_%*5k0c}b2-@G)G-gHG!AV@;SK3kRD&z<&X8StuFog@k?k91ZX|skKY8I7 z|C)XLy7{hr)&VwI;61Q92Dp9O){CV5YC+bp4Rzvp4or~8T~ z6R-Vs0r^foy=BOOR@tYiQ8C1qgtl_ixD~!igDeSYG}>ykSIWJF*Grcy$RS>rUQ$>iZwLz} zxhVuAx|15C!@Rt|X=p98HE1nZltFhBzvPNHpOGA@%p2}VVPUCtRuUoXv`Z*yWzH^w z45Z9f=3tEcPq^oPM0UhO?+cKx{JQE7hBZA7YWDB`3%~gRm~*A>0tx^>u;jI+DP|jL ziVgCb$bb6pUVV1RbhXRp_c<};dw_u#OplNof}KM;Z^8q-)?ba&BKV|+Je&n>*a?eVTj;R42TnxM_X=W!yy0ws4)}`=c`X_EeBAU5JB9ZA17loa^kLwUmoP8nNq6kfSfIV^OzV+%e~2r*9=c4FXT*^l zK0U~Nhdh37uDIknc3ioj=ORD?7&nKB8{sTajS;^9n}LOO@nYPp*HWf&c)k|s5A&OA zNGW+OLynN#`7xx$cBE4jQ~Gw*vkJlhw+TNRR*A{88E@u`A_?2zjS})Dz{u4h3Ve;G z@d`c2s>Nn0ZARKG#MZ6&70gbg#t|Ax*fz-@-V)R_UaoqI6SM$P5egFM`%RF|bQhQf zWeX8qJH-Y+r3E||0rn$$s(?M9afua^d2y1N2F!C^$1w)Wm0Q85H$Ot>hzoNX5nYJx zzjVE)NVYlCxA#W=>?2UZU*j2%7es!Y^V;$9zR!Y;l0~g~w{q6zrF|~<-<0+#->pJ+ z^GT0~{QU;@*x><}AloBPnYj^uVcZ9Yj?6lH_LuEZs*aqAH&HLYj_ty@oZYGQ@&izR zQG*2P&yJ6cRbw1UhHA_yg1}q89=@rFr_YBw6k#XxUAkR06jsO^E}o&GJP)TT?CC@( zv{{ION2EoqICHvAikQO$nIi0E4i2VD9r{!0d+*Rrk}&aAx)(-WgG*)53&=O&-VIHu z9^(U0Eo`Kuk}6D^?C>MPN<&9Mic(*Vk(a89IfgJx-U9Dc%!XYgDT$2r^m$BP@!g30 zxt%DW>%rB$ST0X-NlM*7)j@ed2FE(3CD*8K8`)?piY9^g7e5AXV*SoWZT{RB+p=r% z_9&ewOQNsUs!tu+s~tc7%rd3;l3cNuInrBY}@G9(>5T*OmaGoNX?hKjm<8XqNJ>t;OUIhV8 zk)P|Qhl1p$?4cDs%1FgQq$gI`XueJ5RD#`*rXN4Ee+y0x;ah>+hPhZ`Wo-1U|ByGgUwORMbZ^(D~2nE)I1N9fe zcQJd1metz&=7pbsA9T7bVnq(W{e-ySQ>{O%Z`LfWqxm8g&@oX;Y6-Fi`3dLd2e)w6 zLB`2qvhEjSa`DK}GH5qi<;3u6jPvWG-J%Zn3Fg?xg^;XR_SgAKSSyl3o+_8Cu8|@- z1hXiE?qXbn6e^H&RTYS~@;H19T28&fMSUa84z{vuX1)7H8W2iNc_Zg`TOwxxrnE>`8_6Ga!BX;E8lQ)lB#@VQqmHqv%FRw`*ufKE`8ar$uTtMr zPKMuQ=kiC}AiEY)rCkePtlSJ45(GpYxE-Z7p*+HTI9*hfNT(KfA?Xqu9B;!2XBl(` z>`yIF;L^U^J0t{L$bA!%wd?o#yvEjuU*5RNGe1tpkM&LM-f*O93U)<85l^-8}c8ANrEGNWQO&08SD$;{-rFS=^}F*rNCuYm2rufycl^ysi+>y1hMihaDZ5r z0F+l1-w!-V(YX&!U7>3eGAmUv;_3{Cc814`FvK*VVDv==(;qs5*r_o|<*jFEA@Ih} zBzP4;X*?+XP~FIVS0tf~`jm#p&O_W(x9CoaUl?6o-zYd-v(LsR|4=@#ghr4@phqtA z@7~WNu7cM32AQHU&NYKeDGOlF(9AkNyM`FhexEa3`T0g6`T8fw$Lf#XSB9q4Z2fle z+P2kIoK-Em9G2(QS~qU?i#l>kx+KEOA&0m5Y}dE4Xp@!f;^CT)vRd!`GcqlWj0w$| z;PDQ)`!ZI^1R5sBa*<<7G}9pOXUHw#90L@c_0*Vf?x290DlD5Cn_movwg8n>r9qyA zI!bv!U#xn97|`rTP^ha2r=PJK9agMVI`(nqDJi+})TVJ-a7%;~x0vSL>WsVmI}8WF z1#RS`s!ODk)C^=btN@F|1il(GXeUn~7`ujo>}KcwX7W3!T%@|9T&!@Wjqis@Dd*~E z$5Weuvi=ro5_bVnpY8}vrMOzLt4du1#jxvf#m>!O+=cwe^p=%(ZXbBsQ?jyt&@%o& zX}{y~$7QjWH`}WO-ud$dHkBC>MTUa#3YPcE?(@AR(Sz~6frN>T9)^cPx9s&W#QZnB zG)#ne=_Fy;=rh5HL-+z16PM5w;+!kTIpv+4b}`LMS6WljUgp(0(B-zPr}iuJQYQ+JfiRGgPM`v zn0`j#-bbSmYbFA#{9XL}dgBEparQl7%Im6pw0_B9Sg7*sS&2j(9IwV`FF_e)Qjl^D zY#uD4E`VJ`!)ehS1K9l_zDPvSC{3B2Ecyw+!AL7&28Glt$z6GwgMX2WGa8`fdv7x) z7&d&mrMjRnN7@*gs&vlmFk!ijN6@1Qn}cwj^n;LlOJ8m^Q)Z9Cu)bq4EB{K9xa6A=8_2o- zb(6*AzEyvDj?gta8QU(s#J-*D50s+VstF#Vob|GO?ZbS{PG^DMvdj2QuvAJApfruo z7Uam+!EMl*o?T^dyM#4BmXH$b93Ifa0L5`0b{y{^ttj3 z6d`5vft2!yGhb$^`8YG$6c1U=YW6qm8tLHjhK%v^t1H$Qz>8g%rP5`+9s~Kaai_Tw z5A11itCxjqzH{)qw_S`zyQ3A|f)b+}Jw9u1lpQMfDz?u(m{YxLiWxsSG{vd;AS*2$ zWBoy?U@P?p>7|ZA<;6EWNl%tqbu=JE+um?yHjlD%?L+4Y)4?ugMyr@NuSxAqD z_d_P)`hKN_Z|-*{WYHwlcNl-T7Fk{gIVG)#oc%^exzHkfNY+M=Mp@2DwA^rM;Y*m! z1OZRp4gsw;!J?d2>Hx15{h&(fBBf4VGgqIZO+VAQqBEL$nX|NWx$5>^I8-cL0NYRJ zuL2JMx^)I3Ulh#y+$F*#)fu+9cE!d^ZJPaV@fRj1?np8DXPBkHZZ#(P=)*m7UEKn& zp()?mNY3s3;RBQLEsvYykGa_2sCqPD8F{03RcT+<2OJ+5+%LC$&QV=aV?t4@ur!Hy z`Y&*;L^fd3EG#dr2KVm+efhv;HAdqnN73Ros20<8k3%FeJCl9^eBDf%rF4h3*`YE{ z#Gsdmn~LazDNQ)jt?Ut~Jx=T{_wtpp1^`4S8m$~Mg(&$xjfzp#CcKWTv7Q%E9>`53 zXc4ux7NXv4#?YgCsUFe~KBljNn5mp)o=|zSvuv~m5N}uF`(2kwYdW9-?Jpz@MeY>{MtBT)G-Hb4!gOH|4liYen%1?k{EUgIR?qi#PT2f3;pQDm^iE zDDBA9hc{l9=S$_^{&1o@;^s##XBSTT?koS`M;@w<_pk40W1{!iXgxtq<;;dJfm70$ zu}Vd}GNzNboNk{Yw!KS*aOhTeB__Xg(dR~Y;6{d)_UvyYU=dxqEliQbN3~x6QqYud z1Cd*BSh$D3o<<6Uw5t>9LPf*5UJ^N3Ad_o=)UgN&b)cN@DGlXQ2c{(0(c2P=8C)Q? z%H)RD?-#GU{rKw)>nhzGt@Lf{a+@he$O2^m_+CCvi_TpnAp+rGk(=v=EL4 z$HfF5S)(XjV1(ZY;V#qluo+5U!XG*LYj0ZRt{j)m0UE6t1o~>&Q{havg7p-85CLTt zL4K+eSxF0SBIt3J(6_-yhb+)vCR)=3R6LyGiK^DzCCbGR(4fYcFLK?60e_z&Ve^_f zdb7K1Y?p^yURPrde<^rfD5DY)&hcr)>f;Im=qVf+_UfiDdqne)7Y32L3^ zEC#<+gLItPhxWH$(LRFglE3doq7lvW0MyTC8b?s(kxmKcGlpvVTSz7}**1vKEXxp9 zkW%Ok=u43&$SJSIS$6}Qy28j}yJTbmxWYgFL9+O+=!GjDe33P|UGLU{I*9ouyS#w% z&iYth*JytGx2f^SFV+1L-)-GvXv@kwLg*108~$t>Q0xL4ZMdN<0bgR$N=;?y)5%cW zE{M>|Od@F`3qBm7nY9S4E)O<9$Qzc4ax2s?CO1v(FF42^lByt9ys0LcTpC`1zxV z^tjD3vgGmJwCR-$7ev5%X45h?>Zu?p4dKP$xFobiXu^2OS*f}m%}vP7N#M9BonV|| zE6ozYet9hlutVjd`Sb{B$K{}9u!)3_rbiQZ7A2kDF8W#Dr2jq)^j5M)ZoWA2iY{bJ7yx(_s$ zl$pP@A2{BFa!V_b{Dl~|@Eb=vMZ`^E7`(g=JC8J2OP4Nvp1;Xt#`E2Rzve};-VV<& z3}1YDZjtX2OMvjbkDo{>=BZC|Y9w#-di__g7RET<@I5IzT|HZNxlvql|KJ4Y@wY>9 z#j6#4vSQyCU`GS+j=L9jg68&1sOf0+IH)Vn>OTj}`IfH<_FadDCBBI(K+AoZ5y)r-wAGkMAZ5nt5^;kc zE%(((8#nv3L5>TfY3}yX1IXkJPA%0e$aJ0+a>At^{#hh ztmar&yWZ$EjW2+IwOP1YVYzI{%}l`<5<(j1Z<(GY0d;HoL& zW-w;K<4_JNC=>&ySXY2K(zm$A2(F+A-9??}9lB+2biBE%TPcl)HiFQKV^HjrMBw)m z@roTORvC2xVx|!+UiJqcn|ADa^E3FZ5cFVP5`v|5o53}M@)Xt69(goq*UHAx<6tiZ zQ2@)t#5_+yQje5_yq za6nbNn#0rX@(@)vT~}epVp=r#^D`?CsO|w_+*Dx&KZBonx?;Y^6XG)E0lYb&cbEn! zu)O@mhm{+OJTDFm4x#EIISxOV^Bg)N+K~zYybjm`J97Fv_Pc(k(&vL!c~0X(1tD;3#oSGhm9AKzd3{_iG7Y0(-X3 zyv2)3u`&=MPooT3cJ6FB>d-ce2Lk+hk3I z54C_CNG+GK&ojc{C`3nk46C&4q6etTfYV&Ta$yT)Zk-nx^rk}+%8w*8no<&!mXg&r zz6bm5&2R&NczAToxDuM{)N?K=T|&*YfMS@go0sp3bUmiOBDqJ5%IE1k{5-f3ERX^% z6aiEIRP|7K9MtrJcC)}WI03GefIY1wfe)zoFIJ7n%D#yJ3W|J0y$&hK;0PRf9~`GK zCHE00Zomc8!=KZ>q)Jum?%jwyHTJa#7zg6MAIv%W-@NaSDbVcxeesH70kFEh5 zKv)rphf60I*rVOYEvh2dI4czEFELC40;3M~)oM|uMT&((a#d4?90-OOj+9O|wP4Gk?X zuWIV@Lgy>idtV>f_4QjWgTZ0MV)i+?Pmo?^WJoQDe=b%&e7U$y)BM8M$-9Fkl~t;^ z*y-7~9RsF;>%TO6*<>;8!W;Q+&cmg@#WH@sw^DTcMaF^`U*e+j-vb*d8|YM3q0$V* zwvftE+Tt2;4=&ilQQb$J!vXY#i!%Dg(0d>J4%4K8Adcw@L=?jf5&6r(mfq^izFXjM z$>Gn>JC{QTKknz>TkELr9<}5tR|<^Th-4RomaABFH;D?@;+(9;j9Ej2-YS+-J6@db zWBTnCpKmI>sEm;+0)1OagXoIw@9~Q%IOPV&K8k$DLsI+h`|{3g8&_Vm;kB`L8?$oq z&A@Y!9xc@RTc;{cj|Bd5vT;wB5T-iC1>6{;#Ix2`p0CW~WXxPl&}8jLyKcZFw9|LZ z^N}o}c^ksYj%X}`Rbbg`DTk$4|DN{11OC8PuI?o|d_m^D0H;#Nq+B>yEHnzspWR>Q z@qm`#rtNCI0gh{~L6WIXm-%UCEfFiL4VxNmJO#cTbvVUI|t(w z`7vr_;$Df#)|FZPfo=w|6Q4Nsz6bIzK3@6$LREF3Ls?+#hk*Lt>g^T2x6qn}DPkt& zFMO+vN4ppPP6>4ecV8FUmP>pXBEPS(w7zIsh0W4-3)~0$-0#H&gVxtJj4u^G3u2=F z-J!-jL=O7;6BP^-r4%^$!O(Eu>7WZse!H;5pp#HSqqed$3G;{=^o@{ji!g(@xszy4 zw+B74KEUZMjHNH~-H6R~Pro`Jwpo0kGVoWaU-tZ2!mU^RPDf9d24B^_F0c+YudJ^L zQVsP_eihy+t?(+L9{*r7_BA^0RNQSNkdh3D3b@az)07$zQxZZj`K~k)GY7`KyC=Ds zjuaDQ3H>N+h6;f*_~#D*+&5$?9Egsq$P$Pv2g8YW88tNg2{6k;YO)-Mb$dJL6hkWK zk1-V_q}L?Kwj1qNumkg2xVSrDr5bZpIUmA_Q(AGCP<}|?SO^cS$l}HJJp((W)jTd5 zEc(YfT?33WpHwF*cet*g+em=XxM$QD>p|DO#GJ3^+1*gzE=7>F@8Cl}WJ zbTD#=wGHMN!-cp#xIQj%ElG4=jVU9iAq$ar6f?w(zM~c{5wqV>5#!{XG0pq1_13qe zu3HPf33V#S3sK#WLTQE{?bYF~LQ1)-w$#l^ zLbz0CCapKseR!^8360DPvg?klT%|18*5lnjab>c7)wk5owfEC*j9sIi@_Dg=@hfg* zT#d1sT8D7$#woGiuYONkb!XQ9U#`SX)v7xan??j#zCJOBG~pkrM-D>4PR3hGdB+$* z;f^!{l`AO5Xxzf9sZHcr%4K1$?g~6DuaRh|%6B!mqdY>@B`GY z=B^h|=0Njp9KvO_b~9(OOxMshHl7xu^E2SVK&ySA3C}J;jj4`Ft{5z4ngWeP#_9n2 zc}TndcAD^8P5-E@B;2zBsEkQ87(P}-O{kGwsS7@ZIR$f3Z@G-qSX4b?pZa`rSnsXm zea@#fwH^^04Rn`0d{G98$M@cMFvd!qdZxSIRfF*(K#D}AV!`CF-M2NJb9Fg>*}fSMf$jg=wT1? zM5n8varw?pdbG3vOmtTzBc}97Fj#p?bzA9l2hM3?m7Px*5qU9eXkD&mBAK~ev&eNx zz8e(0vwlYSSd~{71DVPD;aU)oXR1(3gc6uUGgX38w+N02 z0B5J%Vf;nX8p;GvKzmh5;4@9)&uY(&UGVK=8ZEQ>f3HA=y;4Akm`YLxm ze%~>B@79Q8p{KH|`Z~3*cK5w;&{(+AaZOUjyz>WVD#MSin!DR;1#>sv^S5#}guR1Xk)R9JCDZ(MkFAuU2`%LGmMB+#z|i`M={Y08akTAUAEYkO4Y4xAdx)NmIfxMII{j#-S{+LG@)bZwI5`aKyBZ*wacD>1CBo!Wmkvn1T_ehC{&UO`+; z7DVyN87)s?fAYKE(BtsuhEm}W0_)zns2SIDb&G4!kS{ zYl(BIrl?yULB*+(4uWEnx5DU1xDa$t2K7Ly(n-qb=a>+x_jL1srU^=>m7D>qG3aF4 zZ^%0rlB5XT?^VV|;1s$7+CtGq2R$Hy(lWS#0Bt8M@|E}A{H->}lD9Eri3pFeQgz10 zJer#lv=ym$U&651EPp63wGH=#|EwbK^*`iu+_3Dq;E?|~aKb4jq%btxA*OTCq3_C)d!aw%`-jKWl-^V+`wgdmJwv(5H6qTY`>1ZZT7p#h zvg&l-6x4bA)tG@Ck!K?%Be&)DALqNdVPiPyHyRTaShJsBST$s;$^*i2X}F4oenkDd zuIt!i4!>U1A<>!_Ne-C>SZLj}a6QYA`Bi0CZ7#esF{HSenIY{}jrhWe| z<=fmd0pr#|eKCj!S5z9L*JZ;E0|eC#HAkFGZ7-ROx}d^a5}16GonlAf#bzfLpo)a{ zNoaQRqArJey%-vB(x_2)@jMN|MZ?l``Cl+rv01rV^$$Njq`Rstt5%VuK zRon`H?M9#a{P{w6`i*iQyAuT_=BFm%82Wdnqs6*Maf;CEZssMbw6e_HSOsOY@UN1( z!KvI|kd>UZ9tvl83ejmtw@CnflZhIPmfRd3HGyjY{0gMT3R`9GPAYt%qS3|D&p^fy zo{W#+&P9wPuEA_Es|kk=6(NhiAj`>D3&g+Vugk16k}S-3g$i^QxLHU~2|=NDnW~x(XCt?q<a{YXACZ%dO6Y6guRk26W zt!Or~z}+LHOaN`Y9j7Mba+A2BGNZ46E{WQKqensg2@FFTUFoU1TelUW2Ey)O@b*5$ zN*TJ2egujOAn4g8Owo3W>=Ieu>;#E&VOQN&1z8!bDkdFeE~3RqkIWQ}I)FmC9!!F) z8cdu0Sv;yaGDxW4k8QT5>w=Ey(vVQ_0as#|#K7kS!Yt|aB{I}OQW71+Ys<28cNWi7 ze}S!kZ+f_?Bgkt)gv9S&U1BS02zf^Im%3hmy+qoxo?cp=CkFvELt)ZF&yK+VJtW za_KW+fIg`*l-D6JKy)4{NVJ#A0N4Z!6;Ve=AU2}I{5dh)E=8MBYgPB_BBA$iwUnK} zn5(2fveAAhPr_ENV3o&=es^_~0LJh|`T;oD=?ahbiee1+OL3PivZyaZRJJM>M?<+r zm5b^(@QQX>BT4fs%9^4(1X%eZ)%^n1ow}{!PuV1WW%OHDtVlM%39y za6kX+cvwz=#Wh@sVR~mg_DA89H+TL|3ORRv%$Y==p~=vEnlNX$QvsxOUrVfr3$=ho z(h6f-yZk%cZY}>NK6;|ETCrto)TI|Y0+x}t*4}Qoe(R|N{0OX-*Q=hnQWbkT6e}Z0 zsB{1714#NRL6QV$w!?=3T6KC5Nur}lpkEMwRn8IWx*92Vr>O4UBP=R$4a8oxyu6XB z#>`8W+AVGq53on+mQ=Bc)4EX8oF`A^UY37-s((^7G{|tK{<^kgGBfU+Fur6)$sA-T zp+Sr1q8RCe3zA&IW|z3QiKU9Gx82kjf93psQlxOOS}eQ5*!(3n2c@ndva5&iCUCoA zGt$b^bv-6x4M?Tf1~Lveh?8=!((UfSSv&hvt^g<50(}ae?h0#zoo{;Kd=t53E{agB97zwd&p>2%!Y zUsu)bHr-osT;>t*XVK`5-|vmBDn9WiV{_R1dumLrW2KMf<9c}{XM}pAdLm0TaF2sm zr6MHy7Fa4KqzOz~>aCml2*&8=qD}?h5*k)iQ@4+bRI=%mWGkPvPLmod=JqEy7q%i zWPS?)>;h(SzdV9H)tK3mi*99U5D5x+WN--QP0?}DOVOnpuE(L~HX zR#7becZ3yNvy%~24>%ve?fQ@6t6}1Q;Rro)j+Tw7Y?!ACFq4b_Z`A)cM8Gzs8^jiS zraMH$Yx0IjVBfQ1VQYJ6p&;l4^68-0uaAD*z?g3vUi^K1(6cSthI{`@*P__lf4)68 zIAT;=^VI23PG7?K?lbtJu=9_xJ!Y>@?ab5s?S9T-`Nb`R(>Y+Z+zgf#F*+%;7|x6? zRwXHgNb*+L!B`0;fvET43!(fk?i|hv+Py&u_iJKd8h^~|hufui=Hye`HQ zJBlm>O&Y0lUQ1Udww|SVSXEzMq{i%jcTI;doNM@TR)l@OQ}w`Lq;oxkLRv?%EdfNg zofa})no>tUIJKttKpVHfBaaww1$O*Fs^%OSf6Rb1=1_w4IFmA?g_=caL3Vm!5fYz+ zbQ>vQ{t>v$5=u!j{VZ_d&avsfK%@!5UxaDC9LGAAY1(rWDc~CdSo;a`gU_IS`6)ibWn7C@@f%c3lQwUly{Em~lGrp~ zG54>n(yQE;gu=Pa*GMXKBCtceuLvJ%__1`-KY((){=1;c&xcxZ=(bsTuuA)tK~; zApc_BX7LzdE0~g~w2F}kO?*N{=m6LGUS+T(F8RWP=`0DCXQzFVRN0Y8*CeFax0^Nj ze`np#8?)wRC-RQ{VO#&aIBeGXwl_L+o?EObNK=Kd+qqYSKjMW8NF)6`U?vPQKV?z% zOm&T;(*faHi1_DSl^vtE?V5<#zEi)`a?k;>YxytOG*$G2H%jxiyI*$y#?N{u+DG%1 zZ-cFiM}+!osS|;x24PC8|Dp7P;N}{x>0UTeXGTY7! z7;uD-sWFeJEi*9}xgW#eF0@{=Lsgt!CouaWaYe1_Polmj7$u=*%)VZNTSyC0#sn_^ zIFeg6hF~G))rm+g`jf5AvG{8{HPTOMGxpci0_Mm|K)b?RzjeyHt1`=>BOO`v_@`6S^T$?XeC`i>x(V9b$SDTC<-wIcy`+!NXjVwu&$3(P%~-ex`C0eLGOP8Vt z1K>NcZD>7#mVPb}d9+I+OZ6|wRCT0R268zQksZg0jY6Te?P z{8@=|%`7}5Q>775)R^ch>F*O710VgmfzkWQ#i+yZMW&ePm=+3pI)N5JFg5tty^&vq zE%*(+=K@2Cmc7&6DLg7Z;!cM5+YtdiO$nM&2~CF-sM}g$n@PH~SmBO-Qdu;44lMPn z-!BQIAlqW`^&;)A02k2l!Hd%8uNQbmU8n3p#ikuhv_B!=H%V3P2aF%p6rrB1g=&S4 zFe?2%qBhONOao2apZ7rtg0BYy_eM?#n~)9N6@g}`8^Ya89#v!H{;k({imKMj>=lT}&8s6u-X z#j*gzfJ#zOeHB{Ph6ClJcAqv%S z$tk7wkx>dOZ;*LuGS3n{LN(^2RcXW!DKNa8i#;0_YjA7*NBJhBW+kcuXMO((*FhXJ zHKxb&%E(xt9pY`0$sNt3uTx{*R!dYEI#)~?&=r-k+4P;yxLwiZR57qP+}}16jUZ*X zr?~p!WcTjl-yFuG{27~UYMJ8spw(y!7!cHD$ByxHXFrHFaqo@jhsx%p{nSMhm}^W*#8=U0@#KaXSEcMQ)|4jxdOKaH%$= z6Xc{D^d<0dspkC%&u08e(kv6-7p9*}YJsb+!6F~CAi_q>M*NH7YjgJ1wo!jST{`?v zdUw31H+mp3umvI;twmk4x+!*&fR5(8>;fsstDq7!<}p>$i=o%YE0)m>5+wxm&n!?2 zB3J{?MM?BCPlg_$*rvuTbHfFJv%ZwvhRyaL=|6Hj#p1=wd;FhqLuw`&!m-kyadVD4 z2PXKH?fqNae6E*H_T{;NF)LH%4O>(E*>WKaSih1@x-x=}MeqYa|q z#>1i5W`{zFBdIK=@li($IJ2Vyo2cA%s$w6TJ`|+g|CV)Y7_pk^d%C-ew^_Pa zcO2J9O+t%oRwE%}XftG)U2uj?bMc03`cqDiogVbr+*P+&Rgw1_Zsnn^9j^>;>~!t_ zT9P7E-Q$j)C9C#P=0=FQ1L=rQq8!x|o>5y!GsS|7ZYg}AjL;Z~-Tth;dm=KVJqi^> zFFV^}Q={W~yZRZHw5V-;@$1pRM{t2gh` zcR~2%d(x@IW#`NnXo(EQS#@)cq3P;B13mGT{}3na4Qfmt+G(goQ_fJ}7X03SI&mlm z6+4s*Kpc;Gg{4#HB961!HRuIpj?54F@U5M(sc&M*LiTv@d;Ow^EAW61XDxcT<@ZrA z_kuU7F~!UV2gr8}B?6XA5l#izArFGg?=0y4XT9I+pF|wOo^Sw-mdKycB2Gc0#pLVI zEOexI9~=XK=L$46A#EsdpK%9zmP& zSJN+{Xz*Kir7$&UNoVB~1Qa_Ru2LF@u zw%OCcT3HoUn?*|gng{jzJ@OFP%c>C_GG6Drqk_Iw5)3Yjj=NrZa_F0N)ebndYH7CnaZV9@4QGsV{b39HcK)EEoT zMnJJTwi)X#+aaj1JW}q76W>l1z?BJ(HGwch%!G_qcRvRWw zA8LFU7M3tch|;AIz7fTlV$f*(`GHW<2BjA8?Q1yeNKEL=3-!XLjBY=gho zc(nC9L=Gx(QiAyx!JO&CiB~4vmR{2|em7TH6n!4~r}@w$G})n+FynquJ>;U6cotOf z50HNc_ZLgxB#ODnSVG?pqfh(m4xBtBAZJhyN34_`ihx?H?}(CtJiO?p+t#Jy6D3pV zVeH0TVHksxoXk=n-UujXf60_q&-LoExH`^6)fW=D{=Zl#4N{zRK+YBVsdGm<;S?Dsz< zYkZ%JPa2+g!R@N{#(y4hX43yLT6X^%G}(h~=7N7wbhzStlK2?9JG6y&6KKjtN#bA! zGT4hWq5y?eE+Y(b&bNPXmE~+g|3t(aCn&J`DhiQkZd#BU99Q^7(3U%l=B9b)j^NPT zbopkfau@hHxg>NdMWe#}?75GcKX5e~!RX@Sp}^EBuHtoZ&%XmHXu|s^6M6~rr*(iU zjMd;|>rd-^W$1y#@{odmvCd?B9Dk(5F-G*=2e&QivTHtn7UwP`L;0aL`gD8YHz7(8 zZT6r0cM`7;bM!WNG76#-ua{_u(CN868$)(#bVe{@lyfP1(rS1!iuXw66N8H1-q2dN z!TiyipaJ?Mw{R|8;0x7XE{u8@9%LC{O)5KCc34@obBl28(HrNp`L2XNJ{{;6_Wd1b zv2%fcM3hb}`T_G=(44$jiWr;7ccW}j=l|jE-Q!|h-?!mOk))=bqSPSDt_JOFGJ_;Z zq9RHSLTO_}X=KcuqO=_fEk%QW28MQ3}!q;L_9}d0!T$}B(HJo2ArkDP5`S4YI59Il%GUBjp2}rR$E^tW9|2@S+_IyzaO_n0dtFEv+ z*P#s;U%l&l2dx?kdwg{0Fmtwdkm6LQ(MO-<92!-y#N*u)G6`v|xP|y-w-~5?IA6fp z9OAWN`tcIjdyHxU=Z9y0l~ibsFg?~JsRWo()v)+Xd_^AUf;=VJB3qMDORWIs7>}u;x0H(WQlE z=M{nTs%uq62Z_!OB)W6CA4&feNYbTWng;^Kk$SDajaEB;Q}b310gt-h?gWh4JLAki zzEildfj6;erNYhL__R5=i)U)(HGcJ&G?FF~9_ABMuhf^JU(FPchOhwqymQk-#8(vQ znPj%E#MovI>)P}v;~&GSTEm{GH59@|6%zmppcX zQZgP%0_hq@&v?cB_~skta|<+*uwAy2nHye83@1&c8t@cc%G6h1y^-FNtmnOi6>$Fr zZMi76bUML$j#S)g#vhnjC&uVJ#^?+x$VUf;FM+vI>Aid~iG(P;APGs2GC+%=!MJI7 zh2;sIk?CPaj-_rVrVm|uIP?V{A?i}HfVq1wi3Mp z7Xr^ADLh#bB_k(F-BeeXMCnHe_rpverG;~^J3rpbKA~n*b#w+NK;$CibOevTi4=-p zj!=z0OhB+9CG)6Oq5OmkPe{NL41?kgXurJKII1Sv4>#;+G}jmOJ9_F&LekEfn*rAb z;~donIlA8;Xa;CDxbYGCm{Shm+P%r`+_Sq# zvzHolHpUGz7~8&KPH^ke23=Xg>@Th2g!-dHjZ&ibRNRStT0S)8pQQ2)M7?D(|ie{AM=NFV* zD2Qo}&!TwjZZNmv^`9hTd&NqKY>3br=qnV_>@qh!IPck=o@5*n9!A~I!z)slEHh=B z86WP(ly83VC82^mO5lbQhkAKTkufDUD$|5ZUekYcMEcVW;64v;$it8XFOFHZ8m#2mT4|HidVGZM-e3PnU>1Yt>~UZ&uyCRB05 zu=$+@;=(_FRt^xZ*}NsZcB+GB&x1P{XTN#z<9D^%j*qzk-f2Np6&K)*nNW`lLc1}6 z(!GgIB2(pW0oFDQ&Cf6xiKX_hg*=S_Euf*xKih!Zp|tT;vaj8Q%C{Cw$rBrI5Ss6oXS;S()crI{L#$_^#yZCtl`@Whse06jfD9^T@(MCB7%kwW$3QH3;#cbk?fYaXJFq)%d z+ftA}+M?JO7%C$@rY>M*o>6B$cR1f@o7n;^i8 zqDeZ?&cR8eWg2T`kX_Q90=ob1}pQk_t@=J;`PzQtNs;2!TZ966HM~yZD@nw%Sb@h+V1j%8KMg7r(ep zIuty|_*!czRviUW9KE#y?zkFZ`8P~=ic5CpkL;DP)c>?McBGRX)`4SGFlO-McOhd6 zMESOw!*@bZxglkUmUT-*JLdNOR zFK{({27M84zVAipEohnmy?B7ARz+FJgDWt*w)Wd&Q&D-)rHR1}+ka(%CXJluPfAYE z-Oc{WMh}tuv-8omekRNY)(VCP_zVdMeq6U71hoglr*Lmca00XsI?(kIggmHbTUgJ(=zJ3xuUra!uICbHS>2 zV!mOD1k?LoF3TLumqfjRaNK`Iy^^MhTe?;@kz&Nl5AOm#H_Dx+B3h^&8h^L7?E)AQ zx&7f>B5$W~0d-Awq`T|R%fa&7=G#O5DW{J*@bX!ZVh82K_C#Bk>gNK!S!J7f8`T+2 zI*6$+S%iw`&!kr_PoB1tx*RhjevM;jcgYWQCP5W?o|6Fq;`OzJc&%=f8uVqH^(Ug+ zxpF3lOAz*rbbKFr6ym?9Zr7xlKw8wbyS{wIqSi_4>PmdusOcwBM_<#?X{mOW2vFl< z%lLNZmBG6aRoz1L9dnoh zF6ghx11i|2i3u8P&Ns2k8wr}|8QMb1ys`kY&gco@Dl{dKDOP7!G(ZJ-C8}QZrTf?! zv!=@KT0=bbW@pnMha4Nk;E9rjl#a4o@We6dD%AC_C+eUAk`i@KwCa7_3^|OxwbnPx zsj68mm-o9gnAiah9;A)Oz6c=p?78RECbGewq^mLsv(W&0H3|I#9awD_p^%wHTS(o! z<^pQasjwT2OHt)B(Hj>V1c8y}yoaNwOD|E`bfQQ0wCW(e#o; zb(Bz^@xhqwl=SE8RzGSPe{($R0c&w813Uu8eO3)CNFE90vCDhEKN2OXM??9q_TAWy zrf-&7y_q}yX4#upDbqTx-G^QrfbB%_F2G3E$KO$ez>x8nc7o;;f{GkDsI8~4`_a#5!-58;aZVNRRScbJQU zh}6%{0U)3TO%Pog24=fmr_Jeoc+`gZkof4tAXHBLAd2_UaT9xg0wId~WoZw0*q&wt z^$3umQwjfF3=!rF&ES4UxbbL8QxWwu@Dt3-dCjiOj>x|m z?ub!tFiub0Bvxwh$45dkKAHF;>l5VrTxM%khJZc`4RzcpepkCr(=eZpQ+;l%TBc{Y zFZ)K>x|P{X@eefpPnsy$TRF{g zdeGPH@C!S6Xyz``6sjpK6wgPq)xcP++9QAz^DlJq!&Z_b)n)XBi<#%L!vm15-uEA0 zlsZxoY4`au*|!F4^M5ZkF|Ya6AlAkF4-!ZnpwgJ{_cSqqeBrP}z98rs#Q#^0Xaq#{ zuca5&?Wi|z2#m*yn%q=$`U4y8;>UD=t>QvdwH1FVS`R0#*pzPWcyNyP?mki1DyLoh zuaLI=(5G(*_#yO2eGGzFBy4^qR_m>4rhsdR4JXQHjezagf+pfeZ1~uy-@qL;WkQ&+*gxL78#9O>|9sA#$D6>di_1}eiYrJ9e#&H9xPvSw~t)qvV5 z*8VReXXlM3*WCSvd2s;A2Zt#P2hI}dfjpKuZVA18<N?qE;(g#eJj^YRphZ@zp9l zoMeNr(~xra)kD;^0)`gF=zLv5ez?hi{J|UT)4Ft{uU{F8B2M}Q!M93Oyk4IXFxG4* zwZQMbVJe@aH;si}VpB9I^TQ_`9B+Cyh1!-ky1gGxZ}t5$W9xN}P*ot_2;shA-Zp{A z)}5$1?28A`xRXB4d^`Z4vD~LgcC&#M|Yu#DN2Sef01l4XR#iE2l`FC%%xl8-%C7iY4bpE4KI zQ|&RZ>SaOB^!sChbO>mo@DfcFD5RUr5U-?(A?GI6=+|@19_lQ(jWvkI^NZ}gI*9Aj zgx2-_z7@n7B@1^eG2r`Rl@e`EUWdcbekXiA2>6Y1WmGWy0)+l*SXRsg zRhP%e}Tv6eu-XP8x7 zP*H|K;&)&u(o$vw)Pf^}AA96H7lXq7T;aI%xCYTos4FJ5nEHJf_*g8@w*zRe1wCPx zxYnimO&8y+e2MuxAwYXUQ=4du&`J6;<11I_*xcOuP-aT}n%$UxX%+ue9CVGs29UE8 z(J#LMG80X(9gV;5{7$X}Ku+cY3FLI9>gGotbY1jEob)=~?T|k!=<~r}-}NpHMWBxV zd#myXf}hfq#$qTwLVH2W7pL82jm*u>WmjS&Wc_fHi=;Ae{RgN{;75-&sYem3>!vt7 zMFoFt5SS%9U+Y)U^3Kbe{jAe%|ZTy^yAp6rrvXU=h)TP}t>aKkNgL=x0tM`|VF=v=sesam0BYT7 zY>!JK!1&4q4$BT3(#WWlkonk0P{VzF1~-98o1i0-{Y7#N^BWaqN796r|BouK-#JiO z+fv|O)+3b7zrq}*)2FMGrWQ>;H5I2y(~EzWYZrBAp>(B8&nxJ$GP3C8@*CF&rmv0q zfjIkVyT(j(p=j0}lwL+#nXsz}@iA&c>05J_@CIh1ZDrRDql=4HWm8<{fBxjOYWBhR z*_i+WG8>4zE6ltSu7I2e6kniPJ$i;ARwq?3`{?=L=Q%C!q5V*~tLUsKijgE%{)WN# zC9V82GPoCvBAAG>vVR}_Vt*Zvy#_SQRKl$KV+(HTg1 zRpm8Ds5}onu%bc{;u)YNelNaub=!0<0$krYApUxDU1(G9H;5-99t2gzRYNjo)@$GU zQZwqtY3UoLC1w}VkJy^t*sPbox2|Bw$(-}6(BVI;Xqa;CvS9%}1$6WoaaO@Y+~96u z(3vw+i56~$iite>@@HVFr@xLqI*Tn_+~+>4H+eb7_;}4I=^d-p0bD@6FD^l>KU%Jhb4H8e&m3vO^r&>sGK0B-~DgVOIfLdov@uV*O>$BBG$}m$HSuzuJ zfgmf;1=`{Sbc^tICo7m3eDZOg`A+D@14W}0+vv{g9#cv>;b%BFisH@~P526ljH@kd zox3yo{hLHpY>dLV z3=uJ`nPRUuXH(nd5ViL@-lIE3=`pv_y&4 zz|Cm~vl_#oNBBr!4_OX#!i}+EUNZ3*ldiA zNaXf^kQJ(R)N}^+&yekV&C8~96{rh{VOf$9E-_b$S^-#vPuxcd)y&W_X1{8RxF&Ig z)kda=gUhNam{BFOQO{cX`3S+Nxo!$2qYB(FM3TN&NNiYmImPtLZZ&(U?k|fJzCNGR zwQBfd;^P-{C);2yecngGt!yPc?b+P@X*I{a!Yquku-t%na;GlYZ;N9hIUsO0b9`-~ z{>%2JcJ^9}QwF%ld#sjh5oRuUopuB&k2~FNm|*oWP^em6=C(4bjG1d!;Ty=JtwgPT z$G6isU=)=Z)eIhqD#GiBi04rpN|Wb;D^*~# z|179TUeIK0BE?IW<-9k3uW_jvp z6K9*4GzJhADU97YO}o8a9{6Xfy1}!Zo1-zv+?Ov~n;8sO!)`46Lbn& z@^#IkbN_mSN#Ea~o7c@h-yr$tZ_rP3q^}!MwMAV4vByTdJ`1+gW(iMw1n~GK54@J# zq}1%K%^d+DT25~o`ruPH-y!tk`|0k9B9n>TgP%0S2v`^o0st7G7sf>Nx1jWZ+o<=} zI$^+>)4aK&N8&$>EwAO~`CK|r*0Y=`yL5Ts}_>wgDav&jeUP4kueqe!z?f?tCPJ*>F9`t_(N2T+%mudkdj|!`N!JpYff5 z!ER#NqSKOHp4w9)_b^Eib}!J6Z>Q3exFGkfs}Hzit|z68unNHrgBqxE)3Oe?crc(( zdyDt0bqhA%9+WIDHu?!xkrAtz*t41uZf^HrPtOCB%;wd*z^Hsh1Mf{3%Eb?de7!fm zJuzq)k`~QpuKPiw^AmZj^iAxzkwsFU)nKO z++v5Ozj3K)dO}hVq}xum#ynIjQ4(DyEbL7@RN0?Ut!-x$!l_7JQ)hY0)lRM>o7u8@n~XxEK}O>$&Jki7!oD z^h7>Wr64t5;49B+)p=qioKMBcl{^;fNrzM@Nlo(``frN*D>sVxWob7N(ANO&_XuDG z49yyi?@WvKhv>-~sK*&WZ?R3~>bqC3`Ut%?Ju0*#+&I~FJ)oM($=f4-iBmg_TxIg= z5SD|l)mAWk+a&6>qk?z9@RiKo4)Z9>x(kH)Gj}IhHji8vW@In#nwgcxHxn`fS@f|X zm@7^+w<38>B)L9GcWTQ@cFaAm64Qs_V8OGFi7wL?b{Axa_~@a$`>yXO@f8KB5xyriT!<|N z>9Aca)k5_68wS}CZI%FLe`;0RCk`sf*)Qo4{hb5tZKO`*Qlv&f3u}BKbw_xWvuwCs zwSsMcwjC>3zj^7;G}kH=uDC;S?QG!r~_s^-Mj7LdcK)GeDuD#s(_&c?i*tY9o-c^bg$-w zA&sd;6_XqzMVKF`{h!n}>~VIB1wWdwqSkyhZKaoYdJk>|)OY40`mtYk zcse;x&b3voY8?pAeeH=wH;)pQo|$F7)oOt?CU~aDH9!>jpy0pLNMcpc^w?`gdW(ZI zAl?cPt{e_OqxZ2<3t~R>2oxZ4>dTYOJhXeiCQR^4x*hsg`YV`V`+Hf4l2FAANnVbg z5#Ync+K2}Iv$;z;<@TU>{#Y(|xJ;sCk9fXnCi}pYmHO!`=P;Vt6GI+#=lDU;3voxm z`6YoobT1*?Q~Gj_WdFPt7(lEPL0D>Q>@^;Hy0=DRIyhPVOI$cbr_`fTl&G7RJu;2T z99T8#bUfuNkUDo(3Gsr?c5*+EnSuO&v zajM7gz_202n^$F2)h8&X#R>KP&sxG4CBJpNiX>~w5*SFc(!iP)#HR7ss=ew#86aiE zUb*evAsyvh7pyaX)gBk;I@8(0nw$3oE3<2{6JOX*gh@C|%WtHk`%2;(NDXkQ=Oc-{ z0N1W^&1X4HXKl%1{W9t-p6FQoH_VKgtxPz!msLKxPgc0OcvW_7`$P(R-|jPD6wwlK z^?cAWS^%d86PH0Aj1Ynun!yu6yTAxHo9xML-H9(A6+OxE-AQXN^NGK`Int`;xlI0? z{?hq@znNUHps5f-g`U#|V0N$O6F}D)S-8Ev3$}8h#Jn9i>aA=VfII%} zPGzR5)MHJCpUtiLt_~aX3gt5yeeI4E{b~Y$I}n;)rBzcOkgXuNXM5^`%sn+X8c6Z` zk*jBU4Ist4S~8IY>X^en}y%zluW>6xTRJ5=k4TzC2;`%ju^ z!2taIdRW`>d$?`Jh{SUGw_Nq3^zU^Py(OQ&lgWa#C`G~p1>)y$9m)`8^7;!pOa?oB zJt~~l3qXN>jxH@$DdUZ}$6qw5x7&{0!3UBR3w~^Ps!K$D+Oz4d+ z65zr+R-h1%t4t9KDht9R@kG8|26(+bxIP?uJ@iua@ErG$pwJMx;hNt`F+hXw!x&u( zVW8eoXlMo8e%Z{gq2>t>D|e*;i^Zc?w`#(T2h}|e7gFQXTfsWicon~&w`FY#+87N=7JexQWJ^O z)E{B=;7eQ!nSYrJKe7@UW)z_oPNqQ*7lDGJ0>y`#6SGtxeu|cpSh<}-AuR=>?g))E zlMevqvdeH%MZHM!oy!*_foq5)E^DT8hyr1N9@nok8(e42O zh-GGg>(=6+o;cKjWDEwB5s2@t5D<-GoFVbGTQ{*F7X30>xcj?EY>fS{@9Bp~1m4tu z1o13GYu3Tt08VA4+(S=Pj8!JzXF0dK-}Ou!3FbFH*(V#JxN8@XW`z@1d|FI-v+#G& z015gBeYMinSP89R-$eD@j|H?wy?{kqQ2*5?>2bkZaeb3r2vq^l8kZ%s#)qO}7fV2E zoU3)bC@*Eh!%Cvgv(&wAEmXh8uTKrA)`DU+=NZjseH}I;d4)VMPXRT!(bND`=G+J;oPto5k06n~#J zNmi_4jzc$AUr-KH++2HBd#X+Vk_}2n8jex}MB(A!-nVhOdXLUR;9ZL#WCn^JLJuMS zam7tWZ_mcc^(ao&|4Ig+q)fP)A@H6GaQxK-9WBH# z;r8D4E6vrzSMO6*JIRtVAx-G$Vep}+6%Wb;y^uS>yQh6*@{$O8P&tSqKzb|IP~Z&9 z3Kw(HtxTR;OP2e%kywe{&+P+>?Q+X#kC>L~Akl&egDp6J_kqV#Ns$c;ju=}jz-}s{+R>po%=J7KB|9Wd%D!m+j*IEMfj2>-p+a~#x?&L z<{mRP^bYSu@9Go^6jqR8%n}?v5vIVL3j;@oy~F#*j^^U&w|uq4n+T4C^I1Z!e^wDY zF8I=z*2E;01vvrSIv~J`F4={Ho<0TbUx)hx4A1}PA#F`IdYB!-fB}r+f%e*YR)FX% zsB`kgs(94W)cX(dEAZ}&#sh&w1HR-wOK{S!qqy+@=!wGnp7xU8#BhJl*7y^JtzFSE zJP2rw`)%!jttpVOH8%=UbHB4SEV0Tm$3JV*6(LfGcVU#CrFsLFC%Zyqr6dJV6> zS`6;%QV)NCvMj~#_QiV>)M0VX(q<9`ANc`pJer=fEP<(CDlmH0hJShNs{QM`FEU4u z#!>72?ENBbPdv%Go#aY4TK($S=r*ToAD#Y_xW~Q(Ulv+&c#=(=37<1>qVDduOWM^;C$*uTt+Z!Q**v2AO6GnrcN0L0tXx;~ zJ;H4WhebaJsh6yHG=G$#TI81@-04c*TDEfz#cpHWSCU+HOwBw`xy5mhF~n`=vQH=} zg3Vq_b#{jj1n zx6p(NP5vy1H`8uPmAz-H{$o>YQ{`x*n5AMQMd^8TWA2_Qc6Zu8{wQlFpOr)W@b@*D{ zlXhnTpHz8f*IFCGhywgD=j0&vE-eb&aJP}BC<-z6;Ez!AE^P zdCY!#O*Hms(V$^DUR+e9j&$2wf?lVvcm`W!lyo;`r7@1jno0LNC^BSh%8iTutJ{IJrSvsqDZ;< zm2}MqCz-9|c#?%=n%w01pJ9)C9Xyl;cG&fA8XNWG2*fK~~!=A&em&+hYf zSkIeh+4@_~DT-FHy9eCzJQyFZ+eA`|YG&W{%-USOJeD5!vU!V{?Gi&#GP4i~pAKis zAluHAUte_9M*rpUBjaZ5MYb>RxSY$4KTi9qmBVq}IplirlM$y_E%fG?VlkxYJjhOZ zw-Ag-{tx~*xpIa;2c!g61tT^$L}S;FPIF!a$dv4^^eM7i{klolaOw*hZ;A4Xq)o#Q z?N9GXt!6<7bDOfbn;Ql$f9ja~#w1;6#E($E?7#4S=a&b&rw~(zM)MIZv0WjrS`>tm zL3cq%H>F{$s z5qVo89(5@kydIjf*qU`PS)ozV$11l*0$;%4thT5yH0rKFS1VzWH}g{g9pvKkOE295 zWaZog;+n;TV=js}m2$fa{s}wdQ_@JApItf-Rm+%0S>~CE>sK_f$w5^2ULC7Us3Iv7 z^QU>Tcx}}g%1loKej(XBE=vFWg`MGbv;{pRH9h!qB3X!o$C|O|Zsybr>I^lLwr0%; z^lqo>U9$m#{D<`kH4Eq%-&KoyUcUZE&M}=8-@hvy^+xhtPoGDl?q@Pu#NfMjJ4?Q6 ztkW_qjcfD@VpJfAO_`w>i_!ut;5PoACpjPuGO|IN7&?pvo(pl4 zKB!#JFFss--T%O{DZ=Hl@|U{BYeC{o>12XwIWCfEIhFU1-jh_^|7z|JHm}**DAs30 zx|#aJ5DU4F1ui3nb_SS(4vnuFUj{V)pdqT6CS0_`)SUMq{^H3))+>D&3y?Ei6(`2c zsk_aN<&=MgN%RWsEf^hlsr>D4^$b9AEpu#$2Y)3`H_l@yHRWP*j1VzM#72j z<@i!n1t2n>yHNdzpfX)6evUb(`S(JXaA0CM%+CGE43m#;zY*e|FcnPjkxFQwy{Qhk zWj#PvxRexiCyE)V*&naHk!p`1O)ewQFYT29Jb zl~5pn>iKV_=Fc2u%NmD>5dwZfh${%T!k%zmenrj^55S5ON#Zx+S2{paE4sf8gsy3h zujwm4RqRth4=8HPX)qy1bnz2nZkU6oyh4G@%OdcOu$LHY-k-?giJlBvgXoZK+A$ zFtS2IZm3utUp{YgTBX9tp|IPuMHHEC+i`Zt==MWjUbvTPZQD}tI4wFC7Xbis9ChAc z(Oh`AgGZa!li@K8MLG|R?Mji9Q2ZS8qomFfj3+Z!9(!?0&BOV*LC}N%_>)-P@;BGE3>$vnkiW zQ$)2QpKi%h0!N%eiKbTJ|9Waa@Rc+AZiDj4{*BzrD~Xs#3mp4^lom+3dC(Cj^VlZA#q?Jlr$iYDUOY?E4m{^kjsx9Ic8Aq)word;WH^7= zL%75ic$Xzsn>8Qes)J3hUkNy5`?R=bocDl!aRY3$Dl6 zyY2J~iSL~_uRymXP!vYmL{zL_JO4(KHEG7XQ5l&d=JqyA-*JCbLQnLYNp<|+ft};a z3ciC$!%+eNlYYmz%KmqY#;qh^QsYlB3Ezz~UIde5mXcZyS-p|p-#AIV*vw{vt9IK2 z60%nIbwVq>;tHyb1gY}urYOuv5#6e~cak>GJC3{iF4b#y3-rUj_S=%LA2BTe zyOT_D@%5g#&n z&OQJ_<3NVmk~ci=M#;876Tk;bVig==N^{`1XgR7@xvPHak+iAXRoHUTZa|D$pib7C9{JGh3`qV~TEw{s5*KkMV^u4q~OdTfzAM@N9l16L{~51|ooL z)O$7CK%eUmYa^&!ujO6QhYxlb(FJ^wpL-{BHpuCVcx<`eM-1ieu#a6NBsPPWf82P- z%dBv?w{mlZ-;GyKXLae7R8I(eRKWK9#D_-y-5N*!x2ne*e=l+-7KQ6{_TTG)RI4lT zGKDCSH`;_{ks}V*|F2~?Uk@jZJKg}xelJPKukJ#r7F;pgaQ*MRs?;BbW=?Da*UJ7+ zD@bd!t@t)nMbu&{oEEAW`AE!`wkgCZn>^3XzE*Sy9vGiq5>3;-6J)){G_GXHvoBMp z)>H0W-BV;UoK=Ke=c<*+g56_tn*!K|=FzHn?>m&7y_~O^>%*Siyx!~lilJDvkV;6K z6Qkm8 zJ|$l$ukcryA&{RY5A?oH z`U(Qc1c|NA4_p~$P~o?-kdcXj9X}i>xn?9nvM6R7cPe!f*_X+4_qOOOIMO6GT&%ty z<|teeJ=~cSvUAfm~_XMmPk8G!;5>4V=e7P|n~ zluGe#{g=^onJGu>dV~ecxJI*+yN-V<8uPS5Z1$knWW#(uo8aZBW)E}nkm~Z9w5K?= z+>dfOd?@7F-Uxz%FXmZWnargp7avT6%m|YZy9x%n#7wA`1Lz^0&qXG;d!Wa}zED9o zaOVeZNUBU66D~tz5TaFm(Fws!bFYFLit6e?l{zU$*t0xv7mg4$RKa2W573o`ik|;D~pi8)^+30HOu&VP8wy4C>MVM`htm zoqgU=U->nIqK9)nx284tzoJKpO~LPMqvB=Lh{#T%8yn?Rb9418pVH*1N}_D4yRY97 zU%nPYnLZue{;tDnAFEPl8EOPG*40hh^77SmBO6iS;%FnwyKkhX-%a@PV9>by!?kVR zNe6tcI&8T!nq3n(v+{1CZ;9_v3)g+%(G0=)a&wC(?%OnS6F+P*%a6C&nA}wXuA<#c zB_XQAk)kZf3y-A`%be>e8P#L#bN*+@4#>x})cMYa8TYlldbif)-oTZ*Ez}eq(MjlZ zf0`Y3Rd4Zt{6h;@NT}w{XT+AMLuGS`o(9kkm>arZ(p>I~rQs^?wNW>K9urT`g%nAU zuPd49Sq~i~vPwgp+XV~vY(UdIT4(Iu(!Wso3@D3aieMQcmg&w2_bZ7T+m^#W&7MI? zAca7GCFc&*mv3yPs$kIb*rY@xDpoJuE55-N}IG(wt=W@!jW(U1o- zyuLfp_iaP&l5qpoGUv3)&Cv!gqypICt!9as-Z?UHo3rELUa1Q;E6pY$1~tZS4mn)7 zU)b7o;v1&J()er9&dsiS3ez&b5<<(0?tY{?l+E0`(J}c*h5wRf`=~ho8E87dr5CSk!aYMHZLGEZT0w)ZJ!JAiz$}JLiLe8+ zRkiXP=3%Pj_+1diQZ;yVIj@cxXws=WR3&7==hT3(qS`0fVL?u%?DKUBGM{k7#O4n3 zrbCyNloZr)cr;iXCtUP}LSlKTT{Jc@c(dk7VvyNP9bv59jFG35FKHUc!ZPOcfzUjWZz%Yqdy|Wr_%;R@+0IM8^adgDc^kgVDxp5 zkj>)#CCe$~;cMt2gjC6pDv_ri6+K)^-H9-VRYecJ+DQpdif%(*)d-$NZ(A}P0ZF5Q z6*|&r{5T#TUa|n?@Y4Ds_|Um3NQrXy&PLI-GI(QKk!&YcBkdfcT1QEIlkAfjpyiUZ zrsnkN4Ec=1xdpk;r24WR+}Wi#jhCP`Gc*}PNZL(1h1!Yb4vouGObxa)S9hM_Tqq)* zhR3TIa$o5(V0POhgvGCBktPX^(X(pHDBLofEKQy21nxxwIfOY$NaSx87Ecqb2b0># zGT$(_EENh?<>Y_SV7xsgCc+9Sd$Lh7y85IrkT{p>hv@Q2eO>o=(}0Y%9`YmxGqjkV z%$c-`<^--oo#;{|tOzWfrX#H=Szf$3M+z#R(2| zRVxs;G(+w9UOitb`7v{hN#t9fgQ;9kFo6fLVYjE;lf=es=i?@(%3gpPA(3xdw*41bE709g`62!-3dc#VQW$`AUGOKihO&}Z=EJ7>zS*A6dhx?} zIj8bRY3XQarC1LMjHFud*{bZduQ*O- z4K-Ir9V8AW4^}pCFb#+ksn&aZzKmx+^B=XU5q`rgw<7gx1vun1*zWgsd6A#Su=pcK zlp&UA-k}PMPTF6Z_vxM^iQ89ycj%lljCv49HI+DYG6!Y?htB@06TnB{MBUL4K++=uXLky;L(K{~WfgExV7d!T?onYscL7Ou;(Bs%ENXR_`4;YPAgFsp`xL zST##`!WgZ6hXnskD5^LiaEi5IQTbUHO7 zdtuisU@QRE#lK(8^uKWy0EjG+!87@DVd7sh_%P$suNQ*f{-14yA4cl^KRs{gxuy^9;65u<~DkV+R z-fwPvY{g#b>wcuvAJhgvy9BOGJQ-~hkQAv(9I@6BlA>X5wt`m>G4=tO>I~WkwN-gJ zhIyG6k7smkX36Z2JrNz+_j_;78rA(rV^2o`5k_m`N<5WiW`k7hcTc5*OWqTO@1DwP zf{wTfLemt1r!qHN#RGUMgXcUlcB4s_7Z?f8@^!)T{%ItP_TgZ zXy9-9lr)8~lXLk*H0K|>4E%Aeg@4MjTgAAuV}HrA@Ad|op3nU*%hqCX-@X90KY>A5g>aY`2I`=OXy{fa>{@lM*^x)2Gi#luY_fJSv^bFRV zFOG5RrDAE1|MfN}w}}H%Zem{$RjA%b>?33lltildBI&?XG|lF5eI+9#qS?$#mXrDp zN&cfha=3=r%9J?QIKqtt*`7b0Yj73))LrTCqR#5LPX#aZSsoHmX9q&R=C3~6!=&*a z-VX=dxM1f1drB1t7Gl5KQ}8a&o)~<$r<8VponwclO#${4f0Bv^u&1n_pKc7?xZv0W zk6-1L1Q3h<^!Tl5KHuW|m&eb$WG=^eJBURRkKbkZg}M41mCZZo|Im$lfIA%brv<)@ zpoEh>`lkhcCUg)DKlzsf9{yZ-ONq#vT?$qmIN;Ux9aQj_YK(8_<>m%fYTN9DZG~Lv zAsZXI0-E61+@C4!?LKa^lOTt%!({-28XWim*CEO%?k_)J#n{^9%hk^9XYP6jjRb#a&b??AR;8|2=WiQ|Tip9{Rh8%O zq@jN#VDGw21eMuBhF10)w9`JPIiLV+yi0Au&cK)g`5U@Ed{<|VMr zxZ9SLm#9;BBTgZp2HXa33h#`{R{kVj_N2+zgsz0`kM@MxTIbd-TQ7s$0MRRWey1|& z!O6d0o&pZbVf?5mwv{BVWU2ukEZYl{1gZeZsXehZ|8X%mwcW>@QQ(KJD^$pw)S4Or z6t)$sbt3J+sb#1WqQ$dhd|Rx0{Sf!}0O=)JD>y8{0NZ=er! z`y-D@6!^}FUeLMP-IL^1Nk4W(fAF6F<79y*6UBVKhp8Uu*Ul@x4$c*`0O?)rKWaC z-2Q1{)}4}~@Rfo8Wh2xWZYJ`ytBl^fbI<)}Hp1UP z6DlYN$*v+QQ4hhu&Ehr>o?Lw{7c1WZRWPgz#cy$KTx6BB^vpgf_I!V7jMP}m1l4+W zB=hffC(QAy5ZE|Cw4RM#pkn_Lt@C1Z2Diw5H-*p_)CI)Zg}ni$klbPcZpEjiE8lq5 zLS6$Bx3It)rOEdEX4dz!p7lQ8`>wUV?|GlT9IQp++9COd_cVE|eo!51p=lVgT zzpNJgwG|-3Qu&!Iv=`Nz%(;VsUODo%xK$!_XOq-d`nllZ+%y4eP>*ksISZR12yNMLAww~7nAOkD}FCZKNEDq z3=j10o`17pB}?M7h8pMRvdJV0a-__!<&i<>>33q($FHY|K5s$}!%PaA>A*Z$`;pWg zqp2m3lL>au1b6^)GJ(sr-3z}fU7^LV_?a)LVD}7woK^sG*v(h3l@tSzBlf6Zxq?<) z>rzr50PE=c!2SJw_Y6i;yfb(FAq2oO4az;Umi=l;2tf8^V^jHbqJJu1=+AahPJbg} z3rYM;05kkav_=ywHAVijx9MMp35Dlh`7hcM50)YLLHsIIcqz6=O>>FRFRrwzaBH|h z40wsed34*Npv02^h-q2u9=*JC|IIxkt41xbiy(q`8#Z6pVTSfTOj(f+c5HH03VJ{4 zzVLYnc5JL3k30pRF?|PnjkL0FUEONC;YK6I8F1~OY5l)7rF(}-AS?5{V9^Gw6wjKY zFXT?J`vs~R=Go4s(I-OrRUlv9+P)*Pdi}J`?(ITqJy(~$%D zn8xNt=3lCu;JspLj{GHIMgBFKayiz4GVI z$>m?kr49?Kmg;dJxwM?w0_ySKC6|KiFZO;k{zGzUJp$@+uq^OkNpq>I()QtFLj|F* zx;F2n(*p7wzX?T0f!zPQK8I4CcBd52De5w~l;)2pa;IWYu;nbL zlFq$~^be9FJW<`VRB@ttIh;w$dYLxXTw&DdHf|wbqpWf95%J3{kxMJbWu(6U_%G(Y zLsUe&5%~XrL5o*8j!XVOhQgeG8x#bV|18>a`L8nte~v=Ghz|3Imp~I9C_Qb$i2V46 zunEIGuB{?lTPr@el16&(Z)Nw?DLBLMs8AL4_UG0O^+znnvr9)afwWUwnxhmu?MM>5J3R2jS-T zmA>Fy@z=foSn2B~TIKnGA+}o6rJcSl>?Fdr-5R$tP-Ns^TW}23(nD#sf?b%^I9x+iSlgZ9A<$`9x( z@^!ttD<#%>rrysuS$76(XUv~ta zt~V*V?=8RO-CprjT&B|0m0nw;o|2UJnwvY^XOgnya~@E)+Me(4zZd5;6U@C1Xf}KV z2?R`vIM3`2oJ-5fRRIChuoE?o*>Yr6+v(f88JCahfQYFe%0cyE1*^MrdE$sB9{Aqw zfbda7o5kJ8RCTX+ZT^~(l50#ZCA!k`6h}g3&&MH{R>(S5vuu#D@HK#dmT4HU`_-#* zJ4IAX8?X2g^evc*M0E{4M$J+z%66GrZOABjh{FGhMF zkTyc4k&{F#w&U7~Gq857#fbswtA`aZ$f3`7$hnx4OhnTrk|x9xgYgqKz}+iya4-rS zffU;_dl0M*BESSkIgbj322mn=XT7YUuM1f*m8aB7IVWZ3-U=`JuB^P9e{k zYqHws4=6; zK}*GeB_zPH_7-v?vtAIkE&yDd`v)#Q&ON^X#=Cq+fsoiPXpU>JL{I=Fv7B5&vgw*X z*qBAw4Fkn}cL6e|akn8qkv@SU>c($Edqod-m%c`jP!2xq>m|{;suL{0`Nf&S-Vc8h za^Qzp6G_1jI054wi_UU)PNr--`Si<1xeR$FZK!_p)1<=(yGthcB-=ae%NB~>xzG7oM1w^=ciw(D1TLh(&nlo9 zc2^A0KVCMT?(GSSk5Ls|Tmt)>+I_A`Z}B&`R(LySp=;(rh&4(3Kxd_)RuK2FJj6Lm zL#J_KM%|5|t!-BF$-+D5d>*;@LSU714e*C+hfnctpdBct1Efjpc(*r{d&}zk?HT(@YNNIV@9xOVljE=Dx-ofY@erLx6xq6 zT}O$h=LZyVr!0)L>a4wg*&8dk0z}|)vFnI?7I*XfK_APL)TlTh9L7A@78C?OEWj%s z7j)2}l2EOfC;IV$dmpc|hK9q5*N!D}Tbmq|3IuNja{IE`A@0Twpn}9Ib_lHxqbpTj zRl-EFYQyLmKe{0`1fv&}6869hfe z(W&#-S8Q4fgbO1B1hyu4CcK7RaCviPvtQQXOpUI|<8iNMnLN2PE*$}zmbUyDp334O z()+m>tqcJ!C%KEDDpS5%`@Hdenb!~2BqxbFC03bnosK&O zPu35#m|Q=fJ6t%L*EK%xzVOV&DXPqc&42R#i@#oxM&Fai+lDUVw;XA6x`W@M<6=MB z>)u+Vy3*|A>3g>VmA;3@y1t*k^nV~lEc73r&3~UC=h~3M273Yl)vkG^&p-PrTc=Z_ zBnC2HFtyUNdcF3;V#g$Owq7I&$?SZQ9Ch*$5cte*p+_^PL+Fy^v2F%=!53ClzEC+{ zWwKb#mE_I~R6rMF*7!b*W#1vo6aAiJ>gZ7|r-FcXd}ez3BB6PYLa=Khg@cOIf7$S8 zEYf0c>QLl~m0z&O!gUP{cIt--bcj1%!>Ibj7@8&zU0TvfQuj3}x%>rb-Xk7x8m-9N z*5xwfEv$2Y8fb!O|1tu5dG8*N2kaY0AYfT*J!roKf91oaw;>`^Ojq?yBg z5Tg^rx&3>Jr&Nf$aT528Z<*1f6SKwkZ)Pa2R{wJRIOQW+3+U6I%|^%$AiAcp#A6!k zus5AKDtILy&6dPC*rwXG>*b&_YxfbM9`zMgFE1v=7c4E9YE8ivd}c4`hdbfpHu;gc=xsy%ZDU#=dO~Q;9sqEYe#~Bj{Q7pp=*| zO{)R|c3$SXE2pq+fqaMYF*AoZ{P--JnPZ{pN?TtvRA0UL-eDOv>t_xTri5Xpy#~NU zmWeQP=rwym-L|G}R(fbq*vhu(uuR)q5UWx6F83#C}be6m|;upsRP(50&f87@AP& z0}vo9NT=SwkzwmkjA7-7*K>XK0`lZESvp~tPpCJ2881#|Av# zorHcQ^?=MiM+8$Ck;g$#>iUv0+F8TrAh7w~${* zNq#$B*}|LXsSgHR6G?}-^Ait~<<>*AhgQ@La`bC;V2RRrLz$sQYb1lE>v!Bg;nIdJ zLKkj_J?=8G-G9RTKkF_1c`E}g1a~L!HeOvk#{+|W)Ht4L06Wc1!eFADUm87Yz^;(x z$K+Ge+?=PU8=p&+K2SK^F*;$up))oMwjyA!4jguB?HIe2VP|g61@G&&$zoMh9Yz7` zlCY$!7h=7BNJzZ92!PgI26*a<1o0{t)cus-`oRY?x4Vp~;s zZ@9sAm0KE6w`ZS=ZC9t2*~uEmz1Z|hC?Rg+;h5uR!9!R=m`;UZp$1bz=tJQ=&=Vy%7V6JPV6j&48;^golx z`G@~Xr=*-G%L~@o!p=3WVMIHGSfW6eBx&{xVDsaPWWD{KkEv}$4lNj7SXriC_wMmn z#2#GDY&P`qb&lyXFvxWlRgYC3l>)!;%n1*oDdOwxO#*MLb$VAu2JoYA9^3KUj7^b# z_U7?D;ZnuUlg)?Tzkf*mv!Ku4EMM;MhMm}{RLWVB24PJ)wvLW#r|>ivq)iT>B@vOm zN84@RG~ekq^p#esWDn)o_RI0Y7cB_%+Fq&g33$q?+ro)GA?w^7m@jXfoOwRAE9TjG zlh2SvEauhhrlxvZ%w}sc)k|7v8)na+wap|1@3GStP(^fp90`zaot~eBf0WiSL1-$* zNPX|(Vr7ZK{=1MkD3)i18=1Sh4Mdj?3(WJr0Un4hmBSqU`o0;7)_6l{PNOT5 zG1%{C8<2kKvA(doY*LMy;ekjc2n7|QRhE3SYC(<=1IV0SN@i0r1!Oh~1i6a6U{mQ6 zh_c@F#U%?_7ePr?A0lA(i`dZF7|5AB#WNV@f{7V0W|`uH?L+lap_kxyIScj~T_j@)sY7P5>3M55U8NMv7wvRHuE6W(D+p*Uw( zLz%SP4dVkfzl^Co&Df@r%>xlhyB@pZycmc`)1O^=rbvqSRT6FINBi`=jF*Hcw!uhS zfijCCs}+?ezoJyMJ&Uoo>7^j!Pp#v>=}NvgY+XW%8A#5c#~uMdihknxQ5cDB<5PVg zqZF2Z3M5POTtdzniMsFL$aNd;tTyH>yj3db`v4#rpU&(gswwfb>~I^;3O@Ve z*H5aK8l#=uKQ;^X#f=u6eRl4g_0J-}D)WCm1Mop{36QA(=oW>%$&f-J8;dOukUlor zc3{{$K9c&L8&?hDqa_}{ZfF;EhSzp(++448n!WFOY*X8U=uhM&iGOXvAw=p@GlrsK z&|7%YG-xi~!t~{5-{22Mak)Bm*R(R8ouEv#EYI$$eWxWCf6_oCH0cqHb^ci#foUa| zIP&gcZeV`!=nMA=S7;r7_CNbY(F4pKe$TI*k@#8E7|hTI8N8@fYSeg+4l+326p4ec z@E8FggYSCPMca(bxwg9xyKlCuc3%Fqz$QUvVmXc+?g6cmtnnxwk^jm5)NasnC<0aY-nRy>*nB34;RJ5Bw3cWF`|pb&I$r%8%D zr?%rZf!)GKw7QCl&Iu(~JzX6jQ~UD}VH6FX@J|9!Aa?v0N#+|Cr~ln}IKRl<1h9J>O69Az#%Te5nN=GAYv zsBl8^p(e*z+UifT94zBoK`}6g)cY{a2f~|a<{maDV5wT~wv3ZTHELS+Qtn&DFG^)P zMn>FqY5Ca~WtRU-I2>2A>p!*7{>Far|F4?($@lR|MY}6R_9@teP3LVU-@LX11}RI? zsl#S`ItXVVkX03(D8KH5HbzLjgNA+^uaG6v^-hnk-LL!Ewy4NnJ4*2HN4(?TFJy3o z5zVBPid@fD^>{)bdp~&_>g|H^{4lr0r-L(G?fWGxhmdo317&_a(QN;LR;Xa`(~Prn zK-$6WchY(`moVSy+!}Rq`xI7swcZUz-~1%l%G;UD$qM_i9Jve4U(hA3=h+oj($#i; z1b*@vIY=DaY6LD&{!Ec={Jj?IE6T1WtUH$8yF^hO<{kY9s;8k;RQ>=bOEV;&Cxd=3 zr!~6Ng~mvp8|?8`+aAZ^J8?C>_v37a)OPV-0eF_bisuFy3byi})4d&~T(d$++P>2s zgz!OchNYJEryo=$o{kwhP3HW7#@?yRftK!D>)$5Z;P37`>--H5WFehfKdW#_NbX>1 zHwo=Sv+QzXlNoViSP34gpcB1C<@2mZaAVT>I7~dxay@g61sY-u+KV37u2Apvhx{y zv3onT8;(@Cd9-=od?Q>^cKYe07l$cDzko+{^BYh-g*9j$1h**5TZ1VCuRTeIKymy; zJjStiv-?Qg`*2-*ATD5^y`z1b5xuvxmZrWzFDdy#8FV|jjyscEcdl3LaBG#GNbB|& zyW-oozuPeEkeVXhm1%8v-a@}JM#QJyify0?zP8q6m;d@Qn5Jla*1zl=a-n?!oyC0cV)(haW(9UNwCO*o^C47LeB z?G3qJONVxoKR2@nUO@+l2Y`bz?`rpCWk85%Y`(=YP9d8qF}Cm&D`^ps?|y#jDM5m$ z!;x5l_53(%biy%?c+9BRhqm5v-4{Y8>-yVSK@I!Ro6HCP>e-v$WiujAnPyMdn{MvP zRhFCl`pF9uV%*^xunW-87humbsqc`pPH~>&Ww8e(uy(zYN6PJRs@<8q3@$*-;bCO& z5ZU^H4dGl5$hi)cfLU9)q$KU@srCubhN;ZA)}Yv}EgOx-++oL!Ee zio85;FPU6c6fD!MKXrD>h`bFMh6HU3nUugST5}hSDN39d^cVcDm*%sQ;i_^t3dIB0lsczSDDC%R}E5Rl*g8 ztXnn*W?Z=sUVeG`AZgn7GmizH9Z-+_G&-m=qXZUhL_?Yg;)Sl_=%TOpC0wstIEZAp z<8<~XEbkpC&PT!=*CG)d;n;WE#br_i^Nro296W=JoDF3I@J3w6F9X|x$N5 zL78wwj$a4j%-|oXrvj)A+S8MVF0T9R#i4ogQ!qZ*aFhsbJ@LRBDC#VQn_AFdwvIf;l>5r4ikYzq49WCpz7Qb6;GvJA3*-LuX(@ z(lL$h_w?eZi5>{U*3Az!(w9J5YpF6kuM9$AopR(|a1yHs6^z~o$)n&;H>RrHRgBi} zz>2Xamf?ck+t?3%PvCS>cV#1R^i4?e9&m0HK_^KC^dMjr=MMTgN8$6XQ(-F#2}+|2 z!7=)2?#SH?@EX#nV8wg}N$oy*bIi;ny3MWLQ&w74F7KVz9Sy20PmdoakP>_t&_*=# zKD)XDfoqEa^R8LGH%Bz~V)2$qN*dJ6AysO8oE0;Q;bAi9TmdCBTHBf|^Ta^BRo=ZI zUMh~d)<0j~mJzWtGwJJ00#(*uImJBg@a4eQ>s&96es?&Ykc!LNoLLRe=bd|S`3ueE z$ZOB87Y7~mQwHLXIO9w%1~qlik9ODpeorEBzX|zZ5_-EQ%ok4~DYXi+^_d9D1+itR zI@R~;Qln%KQr&^M*|w=C)OCIh8qm#N=iaAKe)NgAv@M;(uy<_kVEXty{VqW56OSOL zyg2%C$t|!Bpt19^VZ{SguagEF%$DWWCxNz>8^~&nuIqG-6JBge&sd?bfA{hjlV{)k zsBr_v%OijSZ=_Q1cFb2lgeFuy+v>@Z>}>L-ZH1=7h5-q{A(@BYs1-!p%`xrqWLh(pMz2%TqWrV+C3HE?s~e6 z&$ZGz5AizMrUI-q!L-P6IdWC$;A=5WX!f{3hrFi9;WpCPAkf^maaa>={wYXbj$9K+ zzL0CciV8miOl=(;f#JtgJTDk_tHY{3HaeAe3Dz0?AMKwL5nBH{n%3+@VG;{+r z6xRzUnJl}S|AcB%nBBbj&He{pOq2Z6?KF~oN1wJ=-b>ixVsu^5Sa%03#eY~u5$lRV z2T>2Etc7hfCbkA{sdak0b8A6KqDAYjyQX?R)K3to z5@jG`>PyeYOr{@N4MLaGOc0aDV=;MS4?!(_lTyEADtt2%D@{CtmDG<_(%CvfQjmO6 z9V?iu+}s1vBMWRBA~KgbqGr~_OIt?mzRvgSsO#iJ-Gbil%F$zg$=^xDB)8Dio{t4I zR1O=D*}gHwklKcZ&Z8%Koyj+$7Ms{+LCFkZ_{d$`-YrbNr@vp?rnt+&UxSnHxVa8Y zd>%79Y%O0Gvg)w4oY3LJ^RLLQCw>!}Dh5p!ixK+pOY$5T8%i)ow@#k9?n<7SMj`9T ztt}y6?;IrsVrRNAixcDqikbfW7>duO4_Z^b=XP9o#m-Dq1)`{#8SGU}c~rV0?*~P& zh=nsg#0jo7{w5TXkFd()Ep;{5j{Cs|8QY__v{(ndKMxky=k~j)-n=@%n`o+IJ~HXp-1A@5jlP0vF{jiE$;7FN+m=80dgan(~XeGAk4 z-O0Eu9EvC*zX81h-WFgnAPMJwy)cwMThY<|*li+vHbj2j7o1CtBX0$#-^2llV9|Rh z+-jIUUjKs7W}%%W=n}{ycxdJbV)5)V7}#$ge>e)OHa=*t7B@ zN2(8Qo*|{<`ewmIcxdpE?^@}dWYWh2D$IcX7(m?{b9}48eRP(U1-A^_EaKf z%@13e^mpZLp+*yU&Vx&YyL2|zhop9nT=WHb-c!H)!kOge=@Sn;?6}|gae>tFEyenM z;#}8u9MN5{8M}I5lv$)*YiVQwzV|B07YkB}PK+E(%sPwR7TWo~*Y1T5n#V~G)}|bv z>FWwf3$>g+q)T5goJ>Ne~*rnczcB$qH7 zs;+Y*5I4wx8^uov#NJ53sF9?Ijz}AnX-K9(JQq-^VWUA$$fllK?*6VyMOB zDI{hiJQTGGFNO>cYKJA)dkLheh<#?+9(7XE`I5O51;wu3V;>9XZ;EMd_IBgUiwY+1 zFTGZ3ZK^r(YPxS-PZ6Kv?1e#(TV-Brel2SLYnV-o!#WoFkQ>n>9 zsUQd2ZK|m09)q&;Q(yBh@{P3xryf$$>^&w+N{4Av_C+?izM$39BGM4Wy-%%S3KJbU z9@=d0di|aNw#j%)jyqv)f4e$Ax6*O(D8g`qq(Ut`9yJW0?h4e1CXVtW$}QjMacgMC zWw2xIa=e-qTYe{MrODx7kVv*5+1`UWl%BX_{QE>n%tbd!0(u?d^pvPl!hdX(Lk+za z6={Km@O5OhO-K{7$V%|Rj?)IU>kAWP8+9fTRi1gi)N?$J`y%U=dyHYmG_ zB%=nW5~$J^ri*T%)|A##?jAQ81axfHZlw7!Pwb zA=9f5CIykRtXwN@8RUJ#3mUqgf!Mis6y2k>SDN}*7_STStB=Ml--kXeDZbo&?X4$; zST&J*U%H*Yxt8?9*u>)`n<xefzB5!&jzYf z=*^-SrcEfb^Mw?z0jXd|;gNkgE8eMN+k)!XZ+`E!zQU_@oP0(N1CRZCLlPzYUlaUFdn{zbkWN`*JPna)A37NEOZt(66HI8OvNaeEa;Wsc>067 zI@WEkI3C!9%{*gBt=#r~)&8nHKgY%urT#p1=ZcbkF*hBql|IRksGG)$s>e%7sUm)h z-tLaQs(}gW6VFR~qq~99S;ylE^FwKs`^rDW!^@(demLe}r^J`yI5*O7nx#Yqj^PYA zCXso1Hi-fyrV=h+WJiY~CWf@4JE-u6uzBFhfok@px-DNrS>5D9?b-tZNuoV`quuB+ zM`{f3Mw;SP(Z+IVm@dPM|dq??t`7 zYK+VUeLeJNsn5lh-kyj~?wyA`{XV)Svp=4jA_lr@PUQA%O!gn;9b4d4=Jxd$;@9DD^jQA4$#}LSQIXD%MlK(;G=A|8dtJx!rbX0(xq*P!lXN~UwM?i+4jXDhnurFeF4Zn4+Wm!on}u|!M%-@hF8CQ<5T&*neAT!1IM zFlL{Qm?v9(e88q?zpBpP4r`D1K@|>&CM?y!HA6q8ZGt|$g3%pzZ$kWzZ_8$3V}R64 z;LgYHlYNgy{4>_xoqsJR8YJY5Sc6FlSDW$VLHx>5l75>ZQ?{Sr$B9UP7!iDp-s>Fh z)^5}~ZpcvWxhsD1`Br1qdRrCRJ;0Dmu_ZIv{bqfKkOX#4-0eYnq)CH{U-kRI7AX!k zG^qaqOUR*LxU^uG`;OIDHxMJBW)_M;mlL$8=^=Mtd}e+@D(rz$9{%NIZcs9JS+67| z4-mG}6$iFHrd?c&9(iLlMKb5#5{UblX(mjjWcX?MQ5AVR+Adkx+7wBXdOSsB6HGLmxquV;}LYu}^qWA?xKBQ|tq! zLWeS7o#CfW;T*{lf5)mkw)A=fiZhaHtgKg}%R4-%>ulW>0ABC(%|v5(sC-pjl|$9z zk^ENS5}2onr-J};iD(6tM&-_e0HS>qq9hunb)x?fKpu|`2zkgC(nZeurH;iHd<{00 z6CP2MLgt{z);hBMq`p8MIW3NJYD~4~CS2;iAzlR(=(BFjZaNnB^>+S9IVVx>+>S(# z0;&YblDDygX#TwKJb5kPZZ8vqVKc-kfFK`vKMyFlbyYlBFMXi@k=_pxod{uy5DEk3 zk4Hex&fPSP6U>qFo>GV&UzDr3zcD6=WS9Z$Xsvv#I?_Is# zb$AJPcZw=+2W@+w=Sr_=sKqTT78NDWBCztUvM3=48?t>7I9 z%dtELRDO)9OOn@bQ_e4B{M$hTrGU^`KgQ0l z4?kgxDJm?*YJiM*aYpEE>m4`J&ik!Kv&9r}hz{gv(q`yKLqZ<;j4x>|gf2XjQCCjq z-TcuK+&GqN9L$=um)c8G8n#SaA#_bNTmY_0A($?|9tXM=0;RCNqwr92NQS!+YuVHx z@XPtKELYt<`9{NUL9Z+$VqLsf$^~r(7Mw~TQ*zX}mkLhB?F&_v=RlMGGO*&*Ge5u| z2IY-JEtfIz5wF_a#NO>yE!tYSlnCYinFvopjep#)V)Fb7ROEn2w?lyb;e5Z;4)~T^ zFsfFzn)(`5gA3kAtLiiiIb)CG0LG$MPbwzpQscpoG))3!Puq^gYDq&~nNY@eAjsZ~7ZI|{d-))5&#NEKEM@bA^=XLq(0r`%Z{v{LBhWx01F z55YV(6gg8zUahwc4PLP8IQ_=dVs7E3(i?I1OD`D3aDz+iSK0hASLKIF=d2zc4q%l~ zLDn=yOY(NYq3uD*94T2;sCdqogn(mAdZXG7NGl;f-ayDe?M+U}sw0o*Oqxub1OCn2 zv_W+;3m#+x)=92%3uth+{vz; zwl^4Ttlf;>LZBa`avUScuP2EjP;c#r!HF9orA}?dQ*ojv`XU-9IbDILTH)=7?|!`$ z%zUj_qXRCDA~dUP{Dyz~1;M6Kxn-0pdwx)$bxo~`wW7_<95R0PCaHU0C`+CSp^)(2_)vJwFUSFjO`8qbtoE3f78xY|2n`^r{Es3$5Qgyd%WYA(b&0 zReM0HF${>e@$?!!U;|H|vWhBc>?p{SP|*Gse|S3IEJ2F!glS_)#0 z^Wizhwc+TZ7ISQOQNC=?cQgBMLq>KJwAE(H1*&3_vg;c2FNQ2MavcyOQ-p>(&~*cd z?AhOob*?LK*a2pzo2$zgl4l+O1qraN)Fsxt%iF?hBk2^P3&m9n3=nlVtr4y9`qM?D zDN$dNW;ry`pWEk~Wo8nNDY>O5{IzMsbbfD=t!S#F{p=5sDaRNrfHR|>svWsSt@?4es|L&&kLT^N{jIQ4=&VSrAnbF^bre#0= zU<&EQ_Ao<>jaq`{PsM}mcS7(MyL=c-9#f^Ju$e8g#ko>{(IJ$RNlFB>0 zNs?bb&T8-SdItb^D3fOdUgs!#Xfgwr&D#%kR=tCnbz^La7S?fP09ExHT_>Y2?NPsS zqvF$=Jh_z?Lc~RZqIN9>Im<+>K(?@irg$23=n00sPEfz-vqfSGJuNl;uhJ5^)M6TAYh1f)+D$(USyw7vXvq!~5Z%Gvewo*{59wC_={&8{)dYz#84J5_z6E~-n2y^=# z_jl8_sd`;hL{-GOv_)a+YW6$d*8Z8CboB>iJPJDad-CHqP(bEgp?0Btv5Wgi9oW}0o-{-W1g0CkJ(qp=7Rnn?o8ny&* zu!+U}(yXxk6T-pI*ST+ALlByu+%_(76e1(X znLXH^G~?lxnZ-$v=Ccf$;L6cg1pR*H+Koaj|9RyY|8eE^HBRl{{W0)>^hw#7H9j)vwsr`wc)j1`?pcPfzwijM2_{8?mYRA3JEX7vn{9u6%yw3ryuFp*`{rC zXZ;u77U9nL^9~+mUqy$2l2V2BFye`er|&g~Jn!F2O1V%@$ccjX;?@{X)sMwh%&J|Y zd%E(Zzb><=On;=HPvhSvt=a>N*9uUj?MC_IA&#}^U0wR2FgiE%g%40vQ?%BY>is=$ ztq6VFs2wYJh8zEIQM=7!cBDAuR#mb7{Ng{z`*iO8rA&*GtHi!&{XNkJqlFRNDQ}Qy z(S6w?y>s>9FLAYlbN+s2+lP@~H#bf17$=$VG(u?>1|Y^%s0P=yTz$fs@u`Rg4rN-A z%Xp8L@>ouWWLky=>+6gH_O(fkm|z2vvAgW|uWC?>7VX+545MENcNzh|__5se_l?uv zJ@#?fefjG6C7oe?1y?~0S)!O4k5)s{;6k<(0~b)eP~PinCKWHRAXwEZa$W6sJ0RoA zEa!HMJ)8qdn_TjP-Ciq#aK503$7mBP7)~74Xjf-oEptHJ%eH z2ll?t41Fsk)3n{qXZ@`e*Ah}1$dSMB)*{E)gM<0v30_u?J>PjyHhzKlI^V<@Ji zl)W#a5?zkY*QnNck%oJkX0jWb>{dc0iDK)%gPA zL8y`Ik$;1val-RUNcMrkPsIsjww(|A^{;9dT+sz94Q4iq3MG+MgP464f>oVs$dW&* zbO!UvEcA1cVqGJ*Da4stbl)YggVnlLLcs`ZYWr6$LvH%4S=VNzSflQn=LTFF{mB_d$!m&M`mmB8X!(bsdJinf3`x;_##mMV^d3w1nm&EVjM(!Yb{+ z$|LWDyijeNW1pflcIs+HtCrcL_5rJ=^6#Gf)3S{akgN1rjz zy}Xk~i|wk)KS%2GrK$>~YhauDh1|wE%2Mt81rcReMWi?O2`ygC${71iNWRZy33v{6 z1zx28Gs+V;{Il{HPzvk*Yvr+Ksq!F%TX#!e%J{0ZI6)bzI1adSFj!TCJ{RZSjaDOl z0K4Og39O<*sQ+FHd*TSN1l{w)ichof16y<99J(U%a2w&$$T|A~sb7t^R~X!Ot6@Ti zRYf@Dcq5zv%b6#u$bEvevihiNLo>(Bf)kA%KPNacFi<2R@Ku-N4GUrT%m(xZnH9(3 zI}wi2@^RbuJCBjn{BQb}ziz&|Aqm%&zSY=sxPIb%{oW6@wiWwF4^HKaE`Y$^ZMV4p z2(GU`(=@k;(LP014N}!q zWqlieRjIRk=`$t0Io7`Bm}&pF1yEH}Fsp)4pcGyJggGdM^C@U0W|8sS$yXhLjxnBq z+p823wO=Azjx3=7Oj;SnkCLDWniOedZQj~GZeGVQRL`VH-sV(L*YeKbOdDce`i*BQ zkv`lIX784Y3@EO#Uw#rg!S+L`0xRy{gjBO41IZCo$d;@|)CY3LL>KmTH=NU;c{UDInWKZYE5b#B_u+qC)&c09D%Hq=-2#M;%W=&gVc4MjW-OMP2#f|hV8f+ zyBH)yBPpTpTaFgszBL9lCNkMDQ*x1h%-vRdvWP0_oq)|uJ6_mh$L?X;V=iLOW1|Jj zU@`#h;mTPNmB${BT~U^pQT3euFek7O?lzQpIyp*-J-s>!2 znhLa6?|dXpm!Me^H~r2Ijbhgl4OTQ0rZ^$f4FBuJk!iU=l;uOklQTgoMe+k>O%c0Q zZoMEsXr_Gj60DA;%I8;V&`a>+$!Bpa%8C(z4ifd$F-QG;NosJkX7}k3oReVPd*jgc z*w0DL2GFaT93KOd$=aa2+=7KvPR!v-i99ynkZ;X~ML9_?6@x`lV ztE5gSuesJdLQ?Cddp}$8t*toXvQvW?XQVOe+ik{Pb<*k{>`Jz?u`fqdLhr=0i4VOc zTzljB3$wB1ZhdwEoL${={RIzp)ZxZRzGrsCmL$BZoG?tL*Cjg_I)4;^t`Yk(4{$+| z01e$V7s%#If_Ky!l!n@ZwbXI)#EM1yKcGGx?2BLgO~`y=eb5s0G8#e2kSvKBpDHc0 z+)nJN3fgu-hk3Q=6xCxc~_=g6z4@?{CM1BKVZdK zR6&;l@yeC75s7kcH!Lmyy+pD+81~f+BLS z+8%vi`FXs(GR^OvnfcYR#6s_!rc-K1X?h#q&UK8}zni&`YWuur;vD%X=(K?Wxv(qc zR0dw!9=58vx8NMX3wSa1P`y(xDDAqqgvvpX4NndBuOBrlN>b|l#V+sr;m}>yeCTYF z;UUV#0&w)90>)ur(GgO|ED;_5f-80e_d#L;CydmvlJJ^sL@W5zk0V)&L7^v--P%Pi zHp}qt?+O&0Av!Ov06(L8U|JQX8jXVUC%z9_O4TY?rVOZe;_xIXUuK_~lP_sQ_oFsL zpC40EYO?UQ#B%WC7_u$&K)tx?iQ(acZM*JUdNJs)spBp=V>-ErtUxRnp5m;iDv4v| zZfzgMB#==-st!$?``ob;{XRa*+Ob!%V#u#IxiE+J_HGjXI(006XDJGik8*|c<%W-3 zIcQ@TK)KqP<|N4gH1d$WJO7uZT;+4JD>v7W3?a*PiDrZ9I9`P z&L5vm(B??Kf#WzfyiKkT1|&Zg=bkEV5eKs`GL*=@Esy;4IR{$fh#Of`JAQN>-PYtk ziWMb$>BnMJEw&ia!HabU)00+fb}ya7H*05bBy+(e;mYr+pnp2jBw1i+6)|9^_X#Zx zhh9i}6mhx|Uyl*J%f88`$QhLCxfwB<((GNiDViBAIQKg*)2F;5-K`pss0-WVaOJ-V zInOi)4jd_Rb$Dku_@wGyV;QL0MVAZeQmBQ3ET{`5D)^{f=ZdcZ@7~J(UMW$cnSzxQ zrN(<--w?U>Loee4W9!YMt6bK(ndZ>Iba*%jpk6Au25jICWDZfecj!sX`tpabeAyM$ zH9XaVPsN~Ym$110_`B=ATE~R&s{ISvt z(snHFq{-9cc^=t4E>>AcW1WOz_=my_EhpByyUHd?WP^GiQ+CW6(X(IxqQ)Ey8QUc| zT7kVRvC+9>)m?C^4oMspItrTUAd!FY1noq_Wq3ud}zAo7s|zF^Fi4CLcAiG$Uy9# z9>HLBg(@7~TM0VbN5GooC(G6+dzdvg{nIn65N`#<+vEh213U>|kdh1F+6jz^$T3ZK2A&?FlQW z^R0JxsMpqc4PSXWBm+s}`adP}pj63cAH4wjto#(?Z{ifU+AOu9bQ-mWR(~eEeH8*A zBhPlRV$JMHD3vKJQAW69D(ZTkX!=Y$DzKlOYB3g`5@GiRYrAMdcK1=q-gM7`saXDv zYV@bHXz!-C&>cJF_HwK~FxiPkYzxP6CY|NHB%1uzFt9t2vVcGRfoY@t(VyymAnxy2 zW-%W#iQ2}-g$vD72aZtxIzZmMDC!?%-k|Ys^GZqaPuQ@`ND6hEUXMfvPLEqzh|`BI zys{?Upj<9NXpT!?5lX&X^V4RXtmgEA`%((UDvc#_KkbdkHVu8&HyApZ?*UKDqE*d0 z?fuU$cZ*2WT*nVC#D|qvLu6Gy4Q88Rov+#sa}`Q|3`f4CPG-7xI3LZ*PGCO3nJv-9 zQJ0}ofjvXSLrkX{Wz;{pD7-rkl*Yne1OYVJybgc3Gdv}D-{+YJ&-|C3iEc60n7>(l z?OWo4?wvz64R7GjPsOt~<2Gm4Q+JH_TKC3;d+(GCYCa?6 zj7zkU3l!7Gj%MSY--jtju|7H;E4HYK{As<+o4~XPKVc%I#acTggMNM#GsQE77f)3{ zNX^Qgw8kRi->nUj&2V}$c3loKvcLN=5`~KMa_E(}B^|TW)`E3gx7ZUhI!@fwWKQma z@RBw25K+jC#V#Wii_M|$)zSCUiYbRXYY)TphKXKA%YC_fcNRVSh#6z0PssiceYaET878CvA|kV4dmzrF zH#~}|4zk0cBJDI`$U0Bxq~{7xL$R@`bp4cY*>#axvpBi!hq-a*1WqXx{X?mGbPGoT?eG7AaH>dQ$(Iy^q#i$7xr>7ib$G08n)?J z@d`Q7C@0~Z*_XKiQ|<$hnH63TtTS zxBiEnGQ*ZXiI9U^a@eT{`_W=OPGd)Z!KX7j--ix-n86S1kgPt6>TkqcDbs7xnkcFjUNv}b*xyvq92-$qNYHy$VwD< zguMl{pen;VEnO8p&3mP$e$prptGa`$Tqcm#2;7o$U2h`iuoKq8v#`q0#NO$1+Qlnr zNi+qQamC45!#Enh@L#FYxHKUt@f>L_<@oOzjQkjNbH(6e#33lqS5R`@{PgfP_?P3K zGA&oTDz_i*nv-sZ?fsQvMbSwZQ(uFk^H#A|j%OQmno_mKvX{XIY++4pkgUT^*Q6pX zUCt0%dmlr+`9-`x*s{;O_nw-kY;4`+`J;@0Y(|OYt`OD8jE4U9=C+WjUo$CYbM-sH zeKM){#v3_>SaKdKnnEJq>q0bgKsCAez`;}Zm&Y+AYj=QmaJ~l7Eg?VUiF(S0Y@p^F z_|lmd4xFj}gMrl~F6W8D(%nO1%N0z-k>#D}U2&`Ly)kyZ1L|Urk$$4+eWPtRHa5)B zn7Xw2MjxRbI0c%<-Y6jFfq=H{&lal;?G~=TZRry;2GJ z17nVM0x)fE_G$;#)HkG%tii2j#@9xd1Vuz1t&U3(xh(hTkmoI4rOF?uNxi9-;pn&J z!_DYUl!=hhFFh+kik_3rI?CJoy7)dhPsto^!~#BXQ@7Yw=)J@B4tfol;pAaKA;$<` zc@X!dorGrp2;>sN-}kNeTV;h-1=reMJU{996>T}NpqE8GZ^A*@N3ZvH0MJYHqGQYp zvL81130lpVi5wTN!CEd=yir;A`jh?POH^M3)EXzgMV?}1;zOA>K4bJG+pO`p9 z+9?{+xl>)uFxb=ot%A~mI|h}9)|5V_$7JjNaB1)$DbyGYK0o$e;lgmBz0(>a8&B+; zr28-Ds^$P^iVkKMO5y=42Mw VK#o909?3B%}M#Cj@f8ve3sc{FMibY>&{4!u{^#fR#nR@M8dH@P5 zBb*XB$Dx}o*a)xec_Of}joJg-*A)h+tI#>4Z0I%CMbLh)<+vXl5ff2dt#?@X$1q)a z7M$QK6^MxvOC$w70fPXo0=*7n$~I_6#vS_q?%4z+5FXT`F-)_)}v8=DZ3YPCp%1%n03CHG}XnNMFBRz;aw3@csD@XJ78TkEbRSns7(V!S@R|CCS;zT*-dhMn{ zu`Tqg2vcwPhPfA7sz}Riy(+wl(=JZY(?05`i7MXt=5}M?w+n6egx=ogB2hZrvkrp2L`(axcOzEyp^L_b*DxC)kKJpY-!rCSdQ8fW(5 zzcPe`el(XoaQoMm-;C9Wt!qz;Wm|>8+#9NzVB&VkL~Z_}p^N?zSC-y)oP3DrnMqUp0>XsiRt6LhM?>d$?+-*0MN!Wj-WNDZ90O)aL{-?(! zwu5SW*NIBvEZdaIA^(v7r^5EM*(;l|?rQRy<-xb=JnVr~Q9p?<9k&&yNoI2K>HsId z+>osdSL7Fx)4A#Z$~I3iasE7g4+#+s%7n|fAMMSH+k7jydMIVBQ>W2-IDJdfM%oW1 zmj*dt5R>#If4bXWL)Dd15aHUyRtizTT=~_TqS{kSl}!>bHIM;&Xfb&C9;osf+kZ+n zI9uPX;Mec`19@M?NSj~cPrOo8MgHF^hfL%S?LutB7mrB#X#htHuvH5;wc}Jr(4Ul5 zLw;l343I4ecUc6Mw{H;dMqcRG(72f&jzYCQa@A60Jm_xKzC=Rq>@$Vb5|bEG{@+Y>CuwbmkUXv zX0ZzAZ)SlKj-(b%^#)&cBTKV7(&JBZoW zNkKgu_0+D(A;*^_0MKNIT}PRlUiKD zy(r7Q8-Nu+mSu_$WcTwdiTe`99_{8-AEK|5px#z;qg6BUYJDIBEA$h@`v#&32 zVd?f-B$RD>no#Q?JbvfLi5{t5`S{+}(ekNew9LrTZ425;4@AWAZ*s?%X1@?*Vo=fL z)NfcPoH{Gc{MzHtKXGr54?YkXY8?$NLjP?uud*LAJ4Rv9z6OefAgo14=$iqubi4Du6L_F8lrgj7-Ix8y9;TB6{$UJ zb-Z87@n$fJy$KDheuYV`gI=5=&5jw2CJ}+iUGF?)YZ!@CZQ#$tB!Y%JohH05rG0H$ zRU#4_p8y1b`e*hgPe#95EhdI>`-?s+RL&@gy=e7#tTSXMR4xKc^dvxwlCJd-;FBe? z@YFRhRvRH?$K#C9>SJtW3ykJCUU{agaFn$gr*)dIAaH=}G_MOMOpGV>Ju(*^q33o2 z8tSw={%gi|Dgh6hBpaQv-;w+X^Le_&RaLWO*}V%x3Y$J4X~wPiZkG0?lZU3VtFRf5 zvgYqr`F`w543hkw%D>Iwd2zn0g=(ltig_OS9D@FANI-g}gc~^mx)ir-o=@NL^ckIy z-ZQPu%MbG0^ErB@ss5X#i23aIZYM&I6Q_*O4;rl#cPcpMWxvSF_#MUk2s*Jbp0rbx zM-;!MlGg(`E5zavm{x7FJq)n7GReDtrKUY-!BgU`I2qcst^0Jf!=m&2_8IXal~oIp zmOz@p-0TAQcXSY}kg9XnT|s!nQLnD_xKoR1bwF*=cZxGK7jyO-6IEYjQ(0WpSNW$# zl7`Q~AhG$g(*H_5ZCxbN1Q#KIojm!;_`iL~&i%ZjC$=M zF$n=B(ZOhf61*qbCA)pV_R4@uB<%nZMp^BUBp_id2{B@(kJ*M6@9kP#{;yOActAGv z76Dbw=NX5;AFiPN&(G$FodUk-Qq1U{cABK}do=mbuEo}Q^v~^M3kwj9yI+)AdpKcw zw!O8rjdnnV{zxrf>{M`%B6I+-c$Nnzlzfz~z0;F+~DEEl=eg-;4#s4NTM~2vY z*r!m4Uc`h5?Ud?EA%CVPXr_bgLnQ>*d5``c@Z}{pl7KwY=&kcTxiIbqo zwnbmt0I#uE?#+vp$KI3K?ynw4e9wL$`Z_yOqGIUI9ld{w4@PLfxYKdj z3Om{gdRxtdvYZkc>(r~iI|hejE!ui7{+6r}6qYK!K5?*o?^Dvzfnip=u%|sd*diK0 zTzKl5rTh3GFD*+xdLr;8Wp$5vNi186#((Uiw^RhM>7YK`M#{!7EHnbB2Pq&85~@sM z_?VReL$WbK74gUtY3~DeyDyoPWA>Qmn0l-?@t`zlNwQ9XYc{|Sc!7SkPLc1a+)7P6i!X5K_!A8 z*Gniz?}(13A4XmSqy-rQgP0C>c#vDLtYH^+5>{s6Pi@F9sb#5a#Ox=zGS{}*UfR($ z-W)m4o~NG(F^_Ovu*jK?=wOdAT?_Dq(SD{ohsTF=OM>%#UM$}Dhz3Qc>EKMYsU%?i+ z*l`!ANzk7L$=0-H;82C()P?7EP6vodqL2+tnQ`KVp`F&fi4HgVr|LZ^cbfhDgi}iO z!Ip`k)ROlb&1k5&%!^beHfQjs?~gRU(XbPZ ziq%J&mVYqEe=V4e>%b{dP;a321`Ns*w4~``Wrf)%OC_}jEYKUsXL{R@!meKO=S zpOL8E^!h1sT{=FkQCkTFSi^%~6x*4@qF)9%Sv?~3T*NHBcHD1O<>_|sVCjx~q6oCm@8WQXiP6!- zD?!G&Z8LsxE_(f$X+16uMr~&I0>QRj6>Q|?ebl^*AR2FGz5@6cQ7Fy9%LJ-0OG%Bo z6i`W^2i#^IdT{C~UXnF602e}YNQr1Iu?m>U-rYg-FIg%62bR9sE;gG0JjE3(1?UdN z3udNJE(dFu3j#~93jA=AI{8b~)Z`3*(v2UT4;D@u_9 zNSA|#+Ndgs?o(=)M0*`fd`HECo)5?{J1L5=Lp;Tv;>>5tgBrv?IlXacC&WvjsKd{0 z?Kh(xUQ~P}+O@0w37YQ4Gt{kgnx7K-1EG0KU1qrknYR9?Rxu)5>LQqF|N! z>S6X|c%fKx=>;Wx(S&j~$p-;*PKaU24XOs&mGh3aN+LzRgh#v=Mkbd5%`{^99R05? zGMGC}z#ijYT*Nzwh|(t;D5m;3K)0@46i8fn)%-p>li8}+eJJJ8<*}Jj>0H8Xa3hT1 z(}`U}b!)rps*~o9Bs!W`7oQlXSryEYqOwER(z^qqiBV22mc5zBi)-sbdKSC`o+fgC zd6|TK-uyIC>VN&&@FUYpCU=i7fGBcRD3F7h1waPon^OwgS#~bnVOsV ze%W(}qCoUq3)=e2+X169;PVL~a)R2tY$=Tdx<22O_S%nuHI13*DxcOicn} z5p4Q-@vi(f?IO|0WmrR?lpRVnrMT3s2x2RWmnkGW3Lm2MKO3C*XSrimp_J4#4aCx^hi?57cAGu-=ycXYd!^c>#-SCTdx8B@LBRkK>22=}M<+LZ^P%iR zZa)v?kD(RBmwX4GSSHZvGf$5jd<@a<;qO=#|IacvK12LqjzOC^gfxSIObKOG{DsnW zJ^}E;&ye3PDaK3na?q>9YPmu9H>ecso{LdJUS++2nHa%4R|Zcdo40LK>Inacny_8o zxK7Y;z<18~!U2tktCvYKkTi(Lsi10h4TDwvQ-!2JiS3qnWr@Mi4GTgdRVo2VYc$i8 zA63}0OyilJiIkMBWi$J?IYkA;N_qk9XC_ka^1l`N+#e$Cf=W6cHf)H`>8_35_=T$X z^M2(&q(q(~riFkUqbW02PYNHZIGxkG2Ce>r9^5um^WX~(1V42%S*Jc61pj5A&YAO{ z;P0YUV>VC>l5f96DXFcNK5m1*`srBr1HYrP72^XX-y;9N{?`B7Z}%!A2B$%$(&WVM zi7v$E_vYY4wDvA~OBb>m*Zrd)Q6e^Y;39~MP1OZPQYy@kM`&R=QK83>V6Zlo*_vMr z(vHKwUm+>*mG+%Hur_^C<|T<#e1a9Dar1$dnmE;KYg_KT+#`kmAEIp(aPQpqbpp!- zyhHx8rAAPQ`vOsHRyo@{AP%18Az-Ubwu4vM@DWL0x;+a5^LP}6F@Zsx*{|@gX-N{f zf2A-JnO48&{`k;=!bdZG3)ok!U|;?C?|miyyRVpk_Z4t_Imu7o{qWS(d2tCTung_A^-h`XAG(PEVE)q$nke8!nSgNvB&EjKB z6-msKIkXS&0Z|L&_YnfjWDLY}0*Z+4E|4L8#zw|mhA2cg75Q>J3nn|TySG0@dWrCQ zs=>DdoX2pGRK(_EC6B0v0Q2-_^D~HR%S#eihHsEAm?3OSk1Ye32${BMDX@&E?)urE zS0!{4qR?4Brt6v_+vCOhph3auaM+nN>!{G=B9N+nX38JacFc1dKZQiZ?=ZN3>|PHb zM?QNL_@zuS!?VThuJ5^DnZ5URQH)*44}R%>6SV6?t^>~45H$)KMWhB#=d|dH@3kWo) z`*rbbOoqg-tQ|2*pOjv75Ziqb0}y5fyp&4aMcLgo!%!l-dGxRhC`XYrILZa;jh&wK ze>b$oF$}L}$7iOfqvFbBF6{?iEipbYrm?9i^+0V2vTy61Y8%6@BgR>q~ISW(dzMNc0MQ8KHd z>bN^st)?z5*3czTsV(s6os>*R2V%H>@F;aYkihI4=Rco+i!PM{_Bm3bCctO`81-BJ zKZRCJ(Q^}5|0F^#ycE155Vqxj3+Kjt5F$`azyQu z9GXNJ=<=CS(-N@~^x}4xm!uiuR>5K5qFbFFkKM273a3A$ z(#)FxL~zO37Ph3%hK8nC*vcP2oaI!enOk^X;QCCn>M5_rpmw3wFSCz56QpK4Aw=l- zSexR|W52rGz(%_29B5wV@C%gHOvG4PtT4wPNyTmwp){yHOlgw225l|S(eEA};Y3GM zxBbirEd(RoyV1_wWyG61hjzf)w>MsT`)4v|w;<;E_G=X1Ml|GwK@9OX#NoopK-SK2 z>ZTfC@J`mM-60s17N|6eUIv>5M2;|AOBbg$AYL3FW$2G_aJBJBW3~bGO4sI@-m0`e zcCH9`$`!c~FGajkfR3TPLUS?6)}3S8dKq&KybajXBDB)}LL}WIWZ5)B)$wT?tRgI) z3>T?Sgcyo9bsWgy%4|Msvv)y~w78XW2=*x#q7)Ip2#s3Y88Eb|GmN*+2hrFIyE0eB z2r)4`x0le_X8{S7^{2B`5fo^1uRK$i>Wz|r`{@}; z4X6IZ>8{@0FTa@jUV9HGpP4*X#snsg{@UnC38vBew2DllO`xmVZ1lOOSwg|;hViHU zi+SZ&ce0_2QxlUf+u$SkSmK*G@XnRTo3eEm!+{bQV$b!c1mhL|AX`9D%ZddJ?yJNx z^?i5bJ3hEhzV&|T5J!;)f%lr)xt(H!l=FGZX!TnG&Oy*Zq8fKr7u_!sIiH42CbU($ z%G+0sr&?tBFRtU4B&AE4tXkg4F#K4^P$MvZv1P?d;MZh~NA@O{`NGQWz#!?V*&vyB zloY?ubLeLXm}h@H$~qpGZJKQxZEotd=Z?MKvE+ApBuz56@!oG{921=Sld(e5O|;Ss z4+)Gmtf#H7-JbiiGTVp_3~>^tCy)Q&*h6Q67I?GoKZ|VyYszQSId2Mg!I&iCl$-%~ z<0HJ#3@SFpwHrbjiEf>pLRX!;ep?;1PM#XGs+xotU`bk$BI&zaa{leG0*p7 z5?GiMlmlNV7B7NZL>Sqig&kV%KG4X-oS;Z9jqAYaaK$w!JDT1;tR`EN1OcKsLCkZ#0ROKqk?K7h>pPpu1%4KWQki84sS+aEGpliG zC6qI14H@Wotl0vGoU5D3@iF>pe&SQtmI673aFz)k=RD#pnu=?CDft%pqv|;_eyIEW z))4G+QJW>`!p_oSd+VvzhR2Mc9KlplragcWQ_9YJle6v*#xFaT_|^m^q|00Jmh%a+ z_Qs~{nY8%vCinE}Z+>fkcDQ)&it4{?KfpX3%SS1e+83o?#6Bt%slNT=eE*Zvw*1@2 zkTW9FU7kM(_wq7lC+$RYVwDHta}f6kwTW12*{2|APxWj`Z`i=V)d|e>VZzvqScthrn`)^(jvE4tVfeg_5m6J zv%QRTM}`aNaEo8fr+K@DT*96jm@`J&DQ68^djaG}baT`oM%4V;vvBa3S8Ow+ai4xA=gec3WKEO-pi zjCiTKcD)pR0TbC%4Bq`Rq{)q|o?7!Fc*N`XyQSox8S2`bX53~OA@IasF5+pH% z8$hwi)quL5p8V!;Jip;fSLxldvBA;&kLf1U=or>kTUvL!{KP=yuBLI9jQ2Xd&OV>| zpXa{#Wi68~R!Q6Y8q(}fpWL%(N|xqOx5DnJ=q44M5q{N{%m%7%heUt86X~+oU}*|) z9mC`HJ0XtP$7B7e`_QH57vD{SNU9FtNmJ~U%fU&sZRuu+SfhZl-3z?}**OB7hjPjH zIR~{@&QtWjQC^yS#`~)8I;p;q_k~JVR!I9N4oEuCkR4+^`NqgTSOp~F{W}^L@^V4^ zXr4gs*rMts+wL?s45?UGR4nYQENns5vq4kX2lVa=IQ_UJ;YOA+K^5uxSb0tsLj3fS zox&cU)}prf#!BkC~V_{c3gB`#uBcM{2_E zyXsLHF(`O{1#-UW zGJKj8Q#x*a^qeSNtY{-yeU*<=spq4m#U@CNC?)9Ucs)z63T~!PNOb$i7rr`(OjqGm z0~s%YsB0QHE97V)1Td!{kzdoEpu$NKY&ush#HWV*${y35w-b`YitUm$Mv)ZTVu}qA zqYvb*M|Lzw^cK;{VJZ8k6D(vR7EvCb&8ijHlWM{n^^V4xPeY~TU%Hy?7ZQ@mHoyIF z()Pcm(&=eYeYQGP8Ie_i(F=ni^aV?#JSA9RDU17>APBdj;}Z{+5x3lPP|nKA2r^{_ zJj?L!xWZgUvBv;B$Aish;mcjv@!_@_oXRW24bmJ&|6`&z*bXD$yiM3jXd0}?T3%1d zHZe`1nLU+DeDRVZ)m3|C$ULnP51l0T8`a9>{OlOh-q;z+Ql)lUr<9}`kfh1-MixcB zyGbe9u64uX83$SFAmB)iK5B3baCp-*esKF?1yCq3i4ee|si79Vi_livg zndns-_%(=Q9P{dv;;N;Jwqj<#zQgRs%Q?s?gQgU$FBsX9YiifMpYd1H=Qsn={ep+8 zfm#k=IIFu?L_A+oMc|hG z7d&h3`nDM|FAbJeJ<8AeeF=Cy2kk74aQ*!bcAo%{)QT)HJv1ecTo$4x+k_FY>2b z*-2c_ho!}erj%WI0o4_y7xA+r6GKCrYP;jAa%iUl)y;WOV$K0IT6x623Rv;9fNyY1 z(d1`39`a&><*%o%gXl3cG{lfTsSCw;LzRhQTjfef*d=sG0!K1srN|rIMAZ)aJe{J! zl}B8Gc$e##bX-2{kU%ly6O<8GmvaWt3LleWqJc0XVm#6`mB_N`6o8JPJaLu{)2|h? zw8(w-rRXL?Kf$B3)}3Tf#Xm7=I-L?qj7i%u)xmVcZHM08*`$laK83kK68YQA>lgh~ zL&k*1wg%^YA^VkOmUpr)ca+`hS&|z_Sl9yR$zzcrwBSM02gh3bJ?HLJpX80pOnrD% z+;zpJrNEpL6S62T`IK!8XTJa$2wI9wBE(+|$d>B}8$=)MqFm;GVYqdDsw$r9o{67E z_c=EfsvlFgO4#@5Jt5)5{E(OQG+1xqYy3pV<58Y`t zDBehYUXaha%g@Nc+~o&)=nWV;ehtMs`(z|{NPoWrpgY3QzUDf89L7ds7{dX3$Kt~?8V#wmT@n3>IO?4)}*T5*38Q_YaJxnZSG!(6pFxkW-OJt}_Fc3iUv>kJX((t) zba#|)2ECUC6<9i9>w3>E`|O7>a8&V24_)F42Z5S;_GOXl)}2j&R&}vvQPc8HSE-AK zQPbLI$GS6qM0n!z$b=jNKgYu3hH@OWgZHxTW8(CcbKmC}WL+)SaJ;-Lpy#D$Bg>zU znV4F{wt!V!#^7394Jv^ssg?uvjIqv!}yeRiGs{3(={_B6-0tr zi;sT>6B5E5pY0wpgK*!id-vf0XmZo0>7&Jzi~|3X%7q0rst&C_ zlEER~h`dLi@Peo#yV*>ERWCujHTsmDUseQ1cVF7R4v>CTH^n@83oBZyd#&Hr0BlHY%IW7pp6Ll-DNc|_v)(S%ZW#S}M$(rA z?vk5_R3+Yvx6J01^6SyaE)}7Doq)008$dR}i&&)62}(~a4<@Jw^W+DJxGkMs0H+O! za!_wd#0S`iuPYecUUHAmyQw*9(wyw;?)RgFbJBr0NKe$(&yB(z1f@!GYRjUPRZ$q@ zWzU?s&oaIyDOEJ2cmuijHin*WkTyI=>z;V}Y~Ia1a!gD6C54?wTh9WJcfHFG_*20X z4}{L|eTM*}=<#<8;SxqZLKE~=^i2QguD!gJP}qRA0Sx1e_4~%brSXuxupK&eNJv8V ze6cwYNkdVLz{bmWr5g2(5zEr+~o);gBjo+iO;Bb|(N=hg!_`yoJw5repD>_`)HM4x zV{%haf{!GoVnaruzj0^mwx;L18zR}sv{R`5RV3-3%HRxPhvE~pO}l`4DLSqnwE6NY z;d8l9#+togHhtPC-Coj%ByBNK(DFEYX-HG%LGQ<#W)${AiT)A@LVk^UJI7`)Xliy5 zxtK>LH5_NkZ52m7^CXuv9uE-~5#KEg)#U^y>5_x_?XiHg=yI_v=B>d#@|kC zD3fgMVHsN^Y)1^;#VU@bTN$8V&3`te6}@((QL>id7{?Rh8{J-fSF{dT{P#hKN|1RCYJ z8Fxw55v3d%e*PN6)&u~7*-olo9g#23H?Ao+3GZU9dTM!QIIdX!bhD4o}phJR%gjk)7UBd0v z2N;6o{9GL)>7=|=qRRf6^aMQrhL15oX)7Bd@MR3KqQh(&(*5ik8PDy#(xY-D__g;~ z|COW{$a4Gi16>wV@l;!+Fytw$nIS4~k*k^Cyf0{OL?VBfOk9VktRH-PrML()sYM!dB_52i$ON;1su-rK4z)ZAB<(-P45AtQ-hSLEmx>a>0M-T?iZ zx`S9=W7b=HTd!Dt$Ox%2$b3R}c;~axs|D-KZ=aMoGQ!1S;Ex$^f>Np48y}uPF#Eqt z5d#mudSN(-1DABYEy%%J2RDyfGa4%Eddiq8z)Y{Z;zhKG7hm*z>pgGfah%QgbZdXHhQ%VP^(7#CTtsRXPoyjp+wd9k7VY9K_?z^@AzmkY zDP)T?sqB?&_M1E&lbwLKoq=y|H1h9(7-9|IMLF_I7v08QrV=}-H5km4lnRM)VNt=P zc%}^NU4^w_O6?xO?;)DF~9G8|HpQ%w#Ee?wLhodlIX)B(PKx`+%E<@ zxVlqc)TNbw9K9eNV-@}jwmmwjr9!{^{g*Ja(o!ziKj%^@Yc8d4)+bj;Kn&ckvCk!? z*m#4))lPs{@f4$W44pt2T;g(bo*YVnxgy78d|lzUt>s`;yY{47sBYl?ci&aksDi5- z4;=#C6&?Q@lVy`;JRy3bV`~mN1&azM>EjD4H8}eB(^l7w(Ncc4VNri@FKyroWMGfzu*eI+j8C6Hf%H zgSCNBtMi-Mo?0Me#I4SIm=A6wiu99U^jGyBHoTmxz3FrKpC>UYVm)oG>aw@%)NbcX zRoFIiuHupwb>oIU56?RF9Dn27*VGbLQ!(XjI!5y)8^xwE@JjyMU0XOC-NeOlK|r`hR1@P*E8nN8 zlE?RjoPOFtm1ztta2yHtG@Ejd9{>LO;R+oCh@?)dZze*n_1HjB3|_ z1EUUnGb;Hqm$<$t4AY0^Q?vW$5*e}Ofg*_F^4tfBa6ke@MLt@3KmE8vCo=bTzEVF_ z`07CJ!}q$K5=99>o8Qpu8420Ooh*4EI^Nun>9XUU&+^(z^m_kZ9J)L+7=U1nce^Wo z;#7OZ*x${%0Y8aobGu-r{>Ge{ZHVy+^PJdRyY!6CGNpT+M;yHasu%i~WRr2a^%Lf2 zd)LFELGvelT~FHGI-HnsCn7Z4Zr5IdAQaoOr{eKoruY(UDb@^M+!ip$Q~=wH&^I@m z0fU(=!;_%hN8p#pej6pqn#*jRa|OL?U%of0wc8?V9k3**b*^mdu!vAV+#9q_FT9TD zdaKv^&K_QRgXfnGVIafCBDdM2U~Qtwt6KMWik(mU<&5$)M$$ME{Vy|kqZqZ!W||t? z|AlVKa=(*gd$}KqM}{9N{wsChK1~+8lhC{YQ+ombT701AJ`=ty(bbfLd??@@X6Zr zlW)o2*QGlITfzn<8g6(U%6@o2BCGs%i7DQd6F_^-m>A4KmLEaT&+CEWW`2CF7OjLF z%ppRBC+uF1S$k2t_YZj4lkN=R*CW+B+%-y^kdSD%*-LgVSqY`{my=_$cFNU)CgYtFUbe~Y{ry4agXY6Z zVI?Jhco)GI$pc|f5ERMgXrMNL!^8t{J}7qG=4`DYa>21KAS~CBeMqHC+$y+GC7YC(W{*cKiGsOQs-D=ZSF%m=6<-E~Rg3=8+-DV<;ec$&+|U7 z%7J=!6B8tBx|#|^ysb1kUNLlznS@r*o-Xs*h`6Zf9w92LUC2Lfvtx71+3<5xFPl$8 z?HmSd(^MT5l)Y>F0Fa>ej(7{~hB9aQe?WoBJ6m_5TA4uK zhzL0}@SqUGiI5U9Nr>mL<;8(ZFSqtOBT4xdBk_e;FDSA3Xm7bYM88fUy$IH8g?xQ} zf6wGb{wi!&NNL%0N2*!w;~+5x&;LgHTG?o6jY;{%GNE?OE--J19bwCc>hN-QDo$aY zMjPG^xm^-45FX&pzpbK?8vo4reVqB3LXR&oiwu5yT>r}E1f0pfg-XWlFmV2<15QGc zu(EO8^3uPY1pj4&lw+#hsvA~~aj&I)zh3u7MNr9MpnS-e58Ze1YJScUS~j*~2gF{; z!*fzyIAijHo)E9pbYZO(WxFcp#<2sYZViFqDt@G^#He7C{FhzPmfoKnksRTc0aC)a z&!r=^o-tQEpW7SRTMivgthC7RG@#)R(dw(fP+}Qxg}F>nD=6EGZ{r)CN!KfI24%0{ zd``;ek`%F*AWPT7TPb`h<}!}4i6|c|J$EtZfNXbym)dUzAW%Uh#)K)9BQ$)6kv&0X zh(;2&gj#6r3a0@WUX#3cQ_04jX_>Y7$@<4)Yp7uR+TDRz9rvcK?;d=~>*RedO20mh z0b-tO!rfVLGn2|J9edl(B_;mzF2$>fjj~%^hC&<%_H@i|ac_z7kL?XC-=e67b(<}O z?mv7AJW+K0{i207nbAiid#+dzHIQ~|V}3w2FP?IE^JlN*he|hCmlVTZ%vupX z^m~<;4^Qz`%R?K+qH(gofC8nM1(1bA=bsiz>Jewbfhg^!5iz0($ApD8$ zO>pzI+RInN^M0}wsJi<1Z!Kt7K`-?rAG@a+8~Tw)4_zx?)+Pvn9|$8Xm({NGj%!#> z2J!%Sux|xW33o~^V1YP6gM$8*vhTp5DLdJnVJHg`sHi--;wd@3}YitFv+$jqp`aDz6+o(~pL!#WwZDI|o#N!kIfUsT2 z*}gX1t?cT_-4{t2Y4~w8=F0ZCWTW4rwZ17wuYN(l@}JlvJ(2bN=Qc?*tyEDx^#!Df zq`Ozq`W6K%B6?Z+`1Nw^3JY>q!^^bbjePSr_l+YSKiG7LS^-6HXefdZpV+}F5tXS& zaHGxoc^{AhcyIIfzbr8*>wN@K$=<@OaGMX!!qH%Z+!P-evt!x>jG33z2O zSl&zw_Bi-Et~w!BwAPk>*g4(xY=+}vL9q8De~^=W#mwrwg686i3u(T*zOyIC*c&8r zUq?$(*U+o4y?WInF(j+(?QXh5h-CmoRJgCxe#kRsxOoHPqufiziOg9Ff`UZeh(<1; zM{S4L?<&-3jgwy7oz=7(5-r4mqaMwSC)D$jg8IL03$Rw)mRI|Hf*rIhbnPo#@P@=e z6)uymtyKp7V46o(GCUYu(#Y zUOHZ)$jx9g9tEN0Eu6iWHo}rd4bdx#2~zf5mz{Xl+cZH>I;QoI!`;@p2-Ulf28W9J z2f})vy0gPc@0*HAneHbaB=^+Z^s4ncURe0-Y3|2dOFv9o>%IX}r-jufv?^Bq>E?G} z)k0va+pjMifc+abtWmAE()0h!^la$P*&|{ntL{@pOdxUcW>x$ETI;Ij1+wNM%4s+p z6~{vVy=q>06PvR^ZY4|IJeUrCRVi%F3r*sp*W{eB*dr|6x;&_@bguSJap60qlUWXe zUbs-mwVQP=z#ke9O%L;#<1^-}#nbvJN4IPRl~{ zBP4%7g0Okh2?&^vquRky0E468yE=`VvbE={9ZVF)#{Gx#C-oo7-#=0q{DWtbG5gtX zd1K>#rBS~Z7Enan$v>lY6vlqUHIafTNW0h0Wh7s1iew~fV|CfPt+3%r^8<@#B^}iz z6TOS8C^oQmO#Wy_(NvOH3sCF(Gm)D=?GCz=>K1qYWZ?m97n=l!cBHTynF$qk>i&&*?S>dRBoBM*826g|KZ52DH!b-Q zVkLz}_B!e&>PKbfYbIT{JCqhcKvT34TOno{lGDUi=D&svZ;B5Wjc22~!+EP-z?BJp zV3@Ubzp$OYAX^0nRS^=0DnBTkfk)_I_Uu7OSL4H^hW1SaIs5zWl2^_8SIZ->Zn3&? zG3lOlX;5b}$!`}G9%_#WIl@ljT0Qd7F#333uE`@aahP5^pZ4ZW#av2Nr0FNSS(BxE zR1~$H{c1J=w&PDVX0lVMXfRES2|{luWNq>hoA+jeXJU_Cl8e78cR+mSPBeO|BUdH$?4t~sysJkH~IAMfM+4teY1{&{bJ zGlujGZ-hlWPyjf+({dXhSLI^Igy;p*iF=<-*uwRKzShs#Z=$w_?!4%A&QY}TRJQLL z!G;^&`yBJK+K%7RNo@Ia{@7WT!lL$WZj}3;mlba&MdhMPr3GoWZGLuk4~N){gJG8$~Yx{YgS1XkifFE94P~XreJpO3)pkx=EeWlzZ;?qrFo*o^^)1 z({?>RGjN<)gN>^&Eobkrey&phwU|>JYcK5lw!QT>b9!CIh$%Kz251EMTZJK09a1QV z`~m0zu4j^{te@m%7s9RF-aqq_<6ec;KVH{=a_h(9HrtNtogUYG>b{A-^xC|SC8>g> zI@Ti{lgryFkwa=@+q5yVtN#o(TLk}!O~b-Rp=2OAK{)}kBDv}gU6)3I%hkzG04e$% z-DZ5i^3J0W&PuZz6}!Ag3`X-lh|JlWDDo6q`GWEm)!UwB220e`$Z@-8DQUwvz=<>a5l7p0=h5a}Ypl70GV;oY~#XmuFX zRbE%r(W3`nw$xR#F8`|$nR20`K>Vw#X7!)fKYHblO&+!eqq;(=5t(U|w{yd%%ze+g zD>EpbF;1^U)74c~qx9pz%Sj)+`Vx+v>W+FFwM5QRU~ki#nI@3G;~b+Z;#XzgvrJ`fOel`6f+b7!|e+jn7h2~Ct8pSmh4AosF2gfAnQ z!A+HSK2~s_+L<&+{P=ZChQ5|QQUADE9<&_#s~6lliV@RkrL~>GV~nVw=qB-USG4c= zMCm5@O}7kE8Xp9M`^9jgXo8|}*`Zj98PH?f(^osBgj*vb<)%kyUic2#!6L@MQ;J`! zo8&Ei<%s03ziUt7wT#!%Rze5BJgWNbG@-gKlWoVMIyCoUtBwMr(z)b-1&FHVwLbNG z09q9xrReRDkrtkTnM(-S02#$l&vkEqR(lGLUe`TdG15wOamtHby8ySi3@`=NT=)Gq;C=taTl6qoJUw16#Q@n(gL^FICLaHp8bOJlsP3bJ+L#XW( zm>7eMl(8jI9!Ml#Z5A(2K&QnHX(CNraCwncYqY2tTnkWDRu;!5UJ~^jET>I#a?7wO z@_I9DVn$|mGtkw9%<=e?Dpd_0_=cV@SlFvd@@BNotXmsL~;|@KGXSk6kF*XzC@5+ z%D!fTtu&mC@VvT{7-h<^r>Kv(e4F%hO)>jmiTpq*VokuW~sthd*-R z*82*~ZR&_zPnCjSJsO}1N*?0-LAe*23KnC4URX$T>7;?TuCxNeKWc2K+3RcLW?!?# zVFvsF)?TBwTET(m~ydno9@^ zn1&^3${;amR)<8Z?ANeslC^y|{ z%8ZJY2va7%vPEWqc^Xzut|A6Lpc*s}d{40hsyyW1E=vpYMtGMO52*4ysIFRkF2KQJ zWyD0a0ct80^*hVYBTzF%Bg_)aMoKAHftuJ3SlEm|$<9(^VEZb`OSYZlgHd3^_R6}T z)33fi>#L>iYNqRi=i&oSMTr8 zOfDGH{=RXbdF};!`Mjek>pnTBv`7y}zZ|hG@`VO2<(*k8?=Q6;syTGyWOjSQyU?Kk z^6rZ2S2FWoL#4w5&YiU#AGs1y6QREl46Nuuau?NOwWiV;CBc5`g<55x(}g3<=B%Zy zg5_6Xkt|tAVdSqsb0tIOYC}d!1PjAT#Q7u8F7y$1(9#qEfAY3TlTv06Q}5;3oN~7v z(e_7smZZJ6I}r~2j6!ABK@b4^}Y?`h2#V6enX<8pDu<> z1~&qm?hpQUpQ<2VGVB5pHJu+fA}oU> zI*mjFaJOc1@L=Ufl!ngoyQn+(3x0rpH^&@m-p&oYx9LyRvxI6RmRE_P^gK!yN1NV^ zpM$(bzH((iKhYk&e-dwqurE+IgCSr?^iPi7Gon*5a>spXsG?0K>W`P7LyLiQNiW`7 z{f0RQzw(S|0p~T{h#cU8f;n@63yb%sz7YxC{u8@6#P|@pa?ZohWJ{x*x23#=MKOVK zyGLsr;y@#z<^_%XkW5;j24+o>{Eo#T`A%qh=o2bXcg(*h0}C6)#?2U~fEIj_dYB)u^7~ZzNw_14eS0^@{GSk~PmJDGM_1XOp_Mxs8XMmY@~LYyUpX~# zd|lx=?^oA7ZnO`b=+TZX8tmDebLeJvRc`C$pF@Mv27;EK{HN-HMD++ikIXSbc?e4= znhg?yjzM#IhU%{49mtU8pvvNyso&VNK-xG%WYUzgAapSpwj$LXs6q@RL_Rao*CtUJ zjJQd2b74OL-8*%_VJ^C!TH3ru7wEhoBl32%tx(;>F+^`FE>cC!d3|6)WiAll7o*n@ z7eWG5$+?xQwuaMav)+bxB;{GAOC91aqQQGXNT^1WS+s))5(JqI2b2Xj1Nr9Qs}j<} z9SXk-iY0VGU;T6n?jQuTXxgXotC8gNA0#Yrl5_?d5`baY;x1(^4RL894>)JAca%FP zUmp)Z_9jZLU6cXV&tvX9I)}NVt@S#Y2k1o1_xt-~d*fk;7VZMZL@Drd`&VPOT}>>WQ3RKbJcMXcyD6M42l!j(x@6Bo_ z*ewI!A(3}R$f%+-jK;^Okh13gV z5^j=6pKe1tf&@=$q{;{bMg-6`k`h`d6WD&hdxW3-AZX7g1T)c{w7)pP=@qmnS+=us zc?;=Ft_07LG7M=zQBK-n9^!>JM@mv~29k10YyQ%%pZB)$eJ{sLW668FYCL1yx}3^w z)GP7J>f-r2=W97Du5#MKpV)`p+H)b{7g@qm8sy3kz`0EfAU~DN?}!tDiz@__K&P4W zLx;^_<19Q*xePc57`Ga^RIjq`qXian8YK<4l$$EhQvMn|mrC364`%Dl6=9z@6KKL4 zcFjLbWzQ?>zs6JF92=c%zg&HsS)ule+&a|N)PJ^-knPr8y02=_5-)PlxfdV6!0cZ% zP~yvbP@=*CZ4vH8NA4?6z?3Ua7Mdbo0`@FjALRr%1N4Tr?fspzGqKc7QpS~^immoPAaj~4 zg3pvWEF!0Qt4?Q1F&NJXi7IHzfSKw0H3O|ju7lz2IxL@*AzI|^*=G-(W%ffLZr`TA zEF8mcK!=cEo*58&w)dxse0!a0LE7XYNky+oxjM8(@E|@Z&(;^s7Fg<|%@?d3Qj(R+ zGA)X7Q$I6dPltJcCHljHB%8|Mo@5Oe^O)7#Rw$(pKOf|>q1<#Apde=;3|Niqtyqp| zzz6DN4j3gNok!4N8(&{QmiTcBE6Kjr}hdgQ8VlKKSa>7k^yQX7;lxYxBZ=DMF2??fQxFA3+WSVdcX+)0WUJ75L!(n`@}IRd$(kqSq8 zXEZj8dx|n(B_~61`C6)+%)Uh0oTbnrZV)%kWs1j45Y15&P=<6QjRr8W`Ne#mfLWbH zGYNVIV;Qss;y+O5*1MwbhOBQhw%zp_onHMeJiIqFW1P5^cG zxR@wxa2U`hq|XfJ)ytFN7Gi&d+N4?G0kuFC)N}ikexZ^?bYEQ^eVz~P{6Eg|f!Tku zc-)lX`DhQ4E{{X+DE2Gyu-4>lWL#1bkK3hK2!i_N289Rx^{@jv2NCk3gPTYLLuy-U zy4(X~qQ=11kUIg+@>KG$4jNU4#!D08LDbY@2gwMgR1q)V9JW=4p+Xtgh`N<#I+ph) z9@%tU#RgRPxV!x{T~)zfz#!EboGS?{;VW)agaWT5|AY2F0s&T)!Ese$dqDWeG?)AJ zaV-64F=`$x;sj+ypCpLiEsPG@MyLhYL&D|zy z|5~CS)RJ{eyUI`v=avLLiTu%1esq$|npR;%P#o~erdt3R(%D^VE0Esr5vI}1V`552 zF2maq;f>BS3=@^Fp8K-783_@rRQqIQ~ER~Y<0S;Zk>_90Ymb&eC|AH%2H|6-UP{qy4a z!hf7|=Clhx713sBjV^T-glgK23C{T%j{8JCvnY?G%yBS>`ZuE|{?&L)X`T6>^U9oA z)v)tqK4(MU1W3(_83u#Yn3g)LV;?Uj4dTOy zLcLJZV#hYJf~Xhj#aT%hG;^4VOyx=z!g`B{N~x}b?7B{-TrLa~{$kJ(J9i3b~~TqVp}H`we2!PS^_ z$HLOlVixrDCsPk&ah8Ja{pO6OI!V4=d$!1Oeup=REOY1sM)g5#&T{nXL;qt?%O0`0 z#|G8Y!c)|nZ1VlgS^v8=KVFq^C)xQIzGXdIl1tZARSgPU=G_o24zH5NFfr68 znEtZ~JI->%ZNlQnr3@bP^sSn!dP?;W~1Etqa%;LRuIMGL}Q4^qCb861A zO6Edx@=j!3TG^*1DaA)$_?B(fr`B>Fo9_4IM(la+-s)eJUwOIlx}uJK5?S05(|yCr zoE7c4cLRF%<8Z32O|0GU0P7va;tGxkA5WU+#&7gaJSYH`cRsl5?(z}SH*Q-WlIPa7 zPR*TM1W5;#2Q(@`&%=t;GDjquBeK%u-hl?p)eE1BF>zjzF0Fl&hwm#xdU3|mBY7bH z>kyRdY^r;HwrlxiV)r4X)Dv{GsB}pt$vb@`%6+}N_xh%*W(DZOL`4|Xd;$t}jclGK zR5?ua1JbkK7f_@Omlr7A(6Z8!u{O!2C!@i>JwGh`ggl82kFXdNlW@R063|NiwFBO) zDeOH##k#WMXXM+oJ(jnbaciXe|9)v${<_%Y-OUlBioU(qN4xQdjRnUE34FFCW9Ez` zZuI+1wkK@?3u->VJ;Azy)typ$$S4DDl8UMemk=$fbQB;1q2-t~j&t8@Kr)I@?}n()lSQX9;6s zQn679`ZEbwUcLqj<`Q(gk-+AfQ)>1fT*OWO_9K|$x(aBBk%`~`)mTPA zcVd*whc|%#=b&Db@r^ukN!JFZBt?l9Uzpfb*6u>P>(p{%=*)L_XD65Lga^iJE+mz+I9Mjn#W4{TXl{_%T1(3M}CbH?`esoR@wx_dmim{b06suXlXz-(WV>XCXQ z&8U}cqp$+J7y^9-^*dnptBqJ!Y=9zBDZvDd=jA1SLpRUu?b-u04L-rOX?w3S^@@N7 zW^2-_1{l~ef#g8y9@L5sMuAT(LrW#wJP*qstL_63;Jwzq9$KUr*f}-lD*|%a0{BioY<^^ za+{yObp7>9D6nOMwYfa8RcRQUGQ8J^L(vOcifGF>*od(pDdi^fXfgUPFsRbN-&1BKhP4yL(UJ| zwAaNU>u1>7>0{kErwVF`nVKM(kH-T01m_ftlBl^4<;vD?$XGu@&-`~&~1kpW3?y=onl+3M0do$j$Fy_MJc z*^Z+i77MhSWvo&_|A^kO&Dkn}Fkjwh13ON(TPP~+2_54uZ?*hCf%u09kw-M3} z;T=7+i#S3-NM2uy!y;;VM)GmXjYqvuF1PIn5%p|hXv<_(Br~+wiH|#9x$ou0XXp7V zq}`$YLR>(5O)+zAZDz=I(&Hem+aGOK8A1HpqJ`&AlJoFgMQf3H%(WM@A62o(ieqTD z{^8NAPDAuI*m#?I9hqRzpr=pFoM1^5B>jq~(kZNRdymrMKFAR`6dqS2Q{VL*NJp54 zecN|PzCS4Ehe>vrQCYr(klFKc3D`4qS5=NZ9s7ZHQ1uj4zGYmU8XyoHD8(`yXl`PM zIPT(VD1{73cO#ddA>+Qz@ibl9k*}?H{E)@*Eu#537bF%@2`wf!M@XxBx#DC|EmKG^ z9v|=)5&-%vz+{kgX#{Zl>y)H(OsVm$0ZyLV{1E$ii5-}#{K-Tgf#X-jTT{_qaAv^a znv^F-zz=IL`EEB5G$0(sCUfCk*!ay}!3W`yW{k#{ys(mV_JQdlz>R0(1yE>z^g$EtdfkO-pbkCJasqQwZ{rc+cyIW3gs=oQ49fSJHH+Wqq-|i;oQc3%i z+kgsO7ueM9w0STl=_^~WM0rV7%GscF>&BaZnT~eo$N7s%nrO7EmKQ8PH!=_1&6Y~xpL$wZY(v_|sW);0jwrPv($?la}dURTz&leBHY6@X!7Sk1)@jE9OD@s))RJ46<;h!Eq|o>h0nwl5;Yz zN@{**jx&GbobFDkc_zYj)R!x+cQ=V@k0D8oLB@<9K(oADNs#!J`%ZkBS);`Z`%gxw zfu21@ zwC?0j%r~7kuG5q|yehp9f4aG6-WqqCH4S^*kY8MJ3b*#Rn8qx0WINgiVuT+$T`@kO zl~!ZnCs;f%K-<$t+g463^g+YK+{Ny5)1{cqZ7bpShVhaXB%Pv7J>Dj9-4L*vZ4uH9 zRL5CjNV(WS?v4Nx9x!5JMDswL<(c(J@KrWOdQYG1L0PQa*v)pTSOy>KyHp;+giBTg zt-8IvUwY>X`}XYF3sf(Xn?yBg`ifYl&p+zx@R;2s7g|F6C<- zH#>4Z?6J@0nU`Bh59>UEGm@!WD+hNW9KO4QxGJqGBlO^l^Iz6D{yO{b8}g_ z{i(cg+EIYztp>-BggQ%(CMPf^zCSr5&s962L4i^`kK;g%NK%7c@?o5*r^FPD&s3D- z8HkY)aWOT^b??Ql(SQ7ecDzTgSL0uE`hvhm0iYo65^XJ-3N!h`CaRlC?I%F1V`UF+ zbr<#fVI;TJ--Up|CNT|bpvu-_+F`^vJ$$!y*kG}dr0?}MpY3}rg42~cir|5QVREo^`<2knh zF)AfX3L-L1Vw3qIIFSnHXsPwsf8#bsl>M!X|1(NjcjQo2WvAc`aI!h~#C(+9`5n#L zB0KJTU2uV*_iEF~A=3Cf_nrH_KJT~e6y6+Cq`m6M!99xWQ!ikEuQ>CT?o!bZs92_) z3%EfkT%F%j3N1cGXGA*H9ePyjiIDoK7Qc`_H#mT+Bjky6Qkhn{TvG)E-9yOHyXXR_@@|f$AsP#}O!I0>fb`k+bw51DB7mUKhN-tLDB!W5 zOc7i&wbd6qI{!#=>#Ot}SftZ+8II;_=*vRg_N=(DD)b+ z3Z3(s9+n&T6?6mISnU~<7f_ij)xCl9q(}g-@WYorXwYmkCh5)Z%Gq0s7d6zNIR95h z+{O#>$*+I(+lqGQl>3a9)rk5kMsi7MmnJ;+`>^!)@7IezSMHAAv-+j=CubW&TOX0f z%CdrML&|lk1nx<{MgzY0H23M$MEy+u0|X=OWtI-vG*J>fE2q%X~a_?kP4|EFZXCChT3k5nf3)a7zqR*yTy>nWH ztW|D&*_)$c+kLVKzA$iZN!+i2r8}1#$=3tG!0m{K$2}xdDP~8@la(t2@HK4gz|3Yy zvWjFjUPwBN(FvZz5B8m#HF!Z_dlOt6HgXoYT{ft|aL0mHhSJsnJ<6r1h3dXeXQ%~$ z-19F(AQAZJ_Xd(+)wtkDLNwE{yf!D=XJB}WnZxa2{7lDR{2W!H_x#eHaW9hb<~@ZW9D#%hGFCzlRMro0?eOdKCRN*K=L@!-2^0FPDxA zWToHQ^a3h!KV_FYuPiEj&9N;msORp!H^F#^)dshSnS+3p7MTZ{qhl_)sWfdk`)J?S zALYg#-9L56e*z)Y`3G3#ME%D-pN?o_^P7uyuG-$E|9Z&niuejzD8rvfkpZDG&A?q( z!JfP%+0Z1rdi{XbMdBf{m`t3b?jM&~(BEKFT#g~G;1tp3jgHYT^#b3rmYBWe~e09ZKb-m!=O;9-P%5WrmCtys^ANo17GV*;$*)7;8#HXI${1O;bY!b3>xOc< zm@>F?{Euh3JP`n({4nS~c5En>Q0sV>sn5!ckg#gClD=P+ZtndSD{%cqL3a|!1oRLP zOMu|JKH&F)x0>i9|5pPT#x3Th$nr^#K2!?NA|AnX4chDH<$4q9L6X5H0 znc;f(@dSzan{KI7=lWQN*#GyBQ%pvU4k>qZb)&D{Fo_cn3c()?sGYA$akz)wB7S>*(&f9Z(O3LF|RSCI4YKQ}S- zpPP7d_=@O2t43o=>4|o0^&00dQzab_lS`py{C2R3utFvDx(7s#v3xUmn1k9=BxWeM zl~d}6q6OpM+vHT3J84ic>Y@~B(!9w9A@Z4*{sFJ#Q;KbuORKMIG>@E{P{f!`-iN&> z|KjQ>4Izr`&MNrmh|+eB#C7$VnYA?xxu8nYDfXWPfoRyBxR?`%5BwINbwxrMq3|du z_Dao==$&dcGNsJK&I3E7_={-fq1x_n#P;7zQ5$fWRt`!bbuo9o);HN0Y4(5Jlkb!m z%m4G9tObj7ArK5o?_@_u0(@ou@IFg(_2A0yj2qDlJ-FcbP0B20zoMkL_#mzw$?T&M zMFHUWeV~SrIkWwv4&a=B=Xu8?RwCE;?rv~+=I!6X{eR-BPR)W|bRvc7R{_~T?~Ed` zqs<8wLxZlgKvw@AM9ag{rWP3E0+ErbvXBpGXpP9SBCZf`4mg8@=~?%)F2u+0fE^~8 zn@eOVKUgU)K{cw9lFQsh&OiGJj<5QilYbjv~ZAG*=&u0a+aR#P2MQQBKkNuPn}6P!A(>{-RR0 zSXzZqY(7MPtMK8w{3 z`pD4#43x(l{?z~%c5V0{Aac&9SzIfWsrBBJ@KoleLxT|!pr#>AGRY79+N9t%@$dPa zBh0F)!VbFp&JiePqlEXEcnNzFZ{=^G<(d_niL2#$Hq3-@#J&x7e%|hsbMlE;h3u_X z8eUsG3q({0FK{-GP^3N9fJwh9T_BQsa!mf!_~M7qIdl5hAdPK)Oto+#fnV18Q>=^YvB&vo}k)yth0G zTwmhxav7KfSlX;uUI$} z2>gs#;0xH>Ztc`)|jUd`*YGmc9#m4|R! z%#XgMIdawr+t==VeEX062Zm#!W+HzXJ?;q|xI0lE*=B3oU)tb9a;uoZ7GD{WkG zzo_bF54dL&e(IQYh$rg=z-);GS5m4~#=C5M;j{EbA)udU1?fEk-^WMkWKhXJ&~NIO z_MMVRsQb!*fhZQYnV1e3!j~C^wBs;?fpd`Ty5A}mrCM59i>jb)sfBSa_JzexapS|YyM8u0_^gZPcYXE=-*yWqB4V^E8%9ru45b^nCi5}B z5x=0Hgm|Ww8Xzs$MQmFscECVbZ_~^*pq)X3WTeHTd72MknWQ75nrVq%8J+1<+6-Hv z`&m+G9{R*0l4rxS%}Mh=@!{tBtvkL>6y=?nn%py1*h%g=tDc4qYc==h*l686ee21O zi^uB#Ae$3=XuVg-%A&4}qOSDLhH6$tJ(k0n1@K5H@+5=2x`TOQu?AlUnzh zW=2@M^8MhuETvWSi(dz(Qn9A%pO_ec;}$PG{?uv~^@ZuE2JVLvhk8m_=sG5!3vN1d zsO~2wnV`W9y#twlaq&vOtX>H}EW8heFg2;#zHgn%FWPBh)9F|*qONBFAk3_hEm-bv z%5L3rxs7`gK>Dx`S9dgEBWb`-o`-rP*2CXPh+)=sj`s7;>dqjktAl`JMKn9}o-miT zVyU}y&$ZouId8ifP~AQLYIy9|wuf~@zpvM`W{!1n7aV$!{Q4;-jT$OkbD`7oxpc(< z2~NA-=vTrDXY_?_xW)~J;$oLRQr((0=d+ci6Y7N-TAA_f*_M&3FAcv>O{UkWjWJCY zPmY*>+OmxC>n_gu4F)ett{{KYl0Zs>65tOfb}&N?WB`jj2&OF7qTWx)1Ra)=02BFU zeVIVUZRT{HjDTUqRbMCoJ#lP0&bfesd2pipKQ&Ftn-+ziWvIdvYTg8e63pq{_*qED zHps#krrj}7_b1N@Q`>*~oINdmI zhhOM>_)C0?Y(Wh`*Q-B>Z!kbEjsoE`;8-cyMBX$Y3 z1*MiszeUk?-$pc^(`GKl@9j(H!N>PiKJ1?r7UwV`l!}ykSfOYsV621zdk!Hsi=cvu|kvWa{0L+h$w17yDhGY_XSOew(#4QoGse+7O9%evEF4!fh zJ82L=L7%WqS_O?aKg?ehdpX|y`<{nWSh~Y)pMZ|rDOFzH%&9&&BH+$lw=}AXrjEKJo!|1d&@6m}hxA6Qxcjm9 zoa~&h?x7=HRgdjY_f5D?O?m<&iND~_jhGf<7GTM#Z7@kd0|gs}=#Do*`Zx>exWnxC z<0RuBgo$7zNS-KN@Bsc-<1UR*;jxfJT$-#f8|$Af@Y?^6x)-YKXl0~f=K*_yIynOc z+T?=B{Plh8U1SMi7N8?R;GpOYQDcZG0r7I(<-^$VmhBc*Ml5JvmtxJXOA9HywftJw za(hur0|t=0&Z!b`WSK}aq)&|jv0RkCx&B%{RJjYd^C6EgLQ8lHoiFgYTdo>{Evgst1+KO|H9&r z_onMNm`v$-%X53Gj?Wy(xxwh9gAewdHVGhFAU`V-~<44Wx-?=a0J$kK7{d%TL z>8mPGuW$z&xwO?xmst`r?2ND&bS!OoH+}^=axfWKWXu>P7Q93vd6s(MqonH{Pc3e> zvf5Y3qwn9?YTvc>GbxR~OFQB9Mz_57H9MRR9lRTLn7t~fV{>gJ`KJHM>eyo$BQ1;U zZTi`LsVj4J)?W8|(S0mGq`l+jrB&Bzo(vb%Pyh6c&4%053#}AbIzPaO-3^6KrH8?N zfV-9oQoy7#?1+0S_PZ$gQt1zURW)vC;+5sx!%2M?Q#67PRARa)dY+`%J3lH)7srru>$;K zlKH-Z^)D^njfFR0zmb}Zp;_EB%w(>CdW{I(CJttBu*&&e1c?m-?(X%d0pOSi-XEhP zAVExB-bdRi9V%3>^mo^|t6Q{?1((XRfKXWg>f&(K%g9m_DY=4Qg6=xR>0%p7fgkxM zq~xkn=OJp)?E7s^Cox%=&bYD>`3| z>o*s5xjV=9%MMDhi;S>IgSqVqr|!!R>3KR`^EIaIvmROd^wXiVjIbs2hXgQs zD0f4jD)h&hwY9l2tO4pFhhU5VlVpiS7^!S;cuIhI65^7TiQJLU%&KgXt91-o4bY@{ z+1`nojgo^>+x6dGu4)`_Tg35x1jd{ipdiLT&J+*0)tDk%mE=Oe?Ste(#<`EMGatK9 z_+V_HqnVO9laqPL1mPy(wprKb>WxM>6rkZl8>42g!KJ(yLL-2qYsp(M7^Y9%B{z@X zfjUb?NmxDm>b4;($$@KyG$%yZV#kH^?70PGm(Qt^DV@5cK_0mE9n$8{%~@S|ccB<( zp*RB!GmR$=z>&bt0YvzQ7W#_k=;Mb;(@-^9Dl*^Kh#q zf0qr2GT9r%H<1$v=$!C>ZV{&MFqM2?dcbDE&%5?V`Kga>g8vRac*eV{FwC*iuiPd$ zoE7nOB#42XL5VXR}|aN5O4PwYRUDu&Xq zL2j*j44u_x#W82mmr)hcz0YOXc`&v>r^-O;)xGb3Nc$y(wc7%XJ+<-9%nOXkOYEHd|wYBmRLdtn^FoiYeob^ zgqF?Gpr>g`?LOLFZLuFn%wn3G$kAoP7KO#OhRWqnpCPU+Z{B=1&m@Xrcue zznqKS`a3#gW%x=^l~0lpMSv;|@AMw!$RtGI+8RnU3;9NE5I}}(JGf6gTq3*N1B`~2 zUbLn53lrD}+CaFMSHE&UROSTQfQ$G4O}uROFedF{i-bx_`Biv&>iQeKj-Kn}d$-v`U`v2W48wrRZI5rs)OJyJ`L)GJ8H?1%XjCxeh*NiqYPl2RfS;;ah{XG~^2n}ge{&&Wk>=BSck?+-GsGmN$*?Vmyw+=EI;W#vAgA;Q z8B%$y_YoDBAbtS-6U<{+(qFF+6&arD1Hx#vg930g14N9EXXaPEj%KzLR@IAHN5z;9W@g#x6}peJl2WLcR2B@d4|b z?maZOLx&HA)|Z3+kYm~PSNcP4N4~0mf*xGlY@9ZX8eGR6q%HVKcwhNZ?KG{G2$~s3 zVWBF0hM>+n#I@vxK-%<97v1>#9>|?mulnB13uTF&6J-@|?i(K&m~XNy-2a(ecp^=e z2y>NbzoCt%klVi+-TS>r6>@yq+D?y3kIQoRb1LT#V~+~bG?!%TEZwkWVNrIU#-eP- z36FhE8!NBML%7CzEpNB0j*8Ylm7K5YYe0f>JEE>vd z@bYb}r!M_)Fm~-JOYf4@#%j^@aPF_^z9xVld}02RtTNupg?p7JrvYFIsW1I(1|yM2 zR0)L9=k4GT>SV@T)!~0NzB4Oar_?6na!(8BH04U5dhEY{w*z3^f_%y`LUo%ni=ilD zVxV6;ekvPL(qRN5q2BOFhkC<@={5)@{Sa|;Qa#cLBO{nG%CE@OPSD>aHsYsTX$ES` z$BOw-=cQZ!StEcSya7w}F-8nfk0<@oyS&0&evAJQHyzgUUV^a*!luX*H$|foY z9hwMA$N&1Zz^W@=Z89Q1;7T}6x^ij;YAHfv`am}mOZxuy7{T}lG@xEu1NZ`8qgi#N zNic_fQxv9qZguzcT|&3oUrOVSeOE?SIOq9Nf_a$7p!1()$eow z=tKm`>k4Vc=-;@&N7Tz*7Q1|Ea$7i?;14-QmtHJSSMMe0QltE68EFp!yv!>HZyu|- zrph9m`dT6nMi&*nrL9D%5RMx2v!c6+JC}o@+2?F>p1<>4BDk$sENwna6u3r8LEzN;O2<+Ys6456 zoH*~hB!i@d1GKvNgm{|!l5rUTFow``Ue9JOsTVpacGEivl^soYDCr~(@SzO;W;K}P z<4taoT0ccDD+IZAMQES^yM%6u&U2iaN8Q#O&#OXNe6>}!U}f{AC>e7;mDD`qmLrvl z1P6N6hId}mKy05of^5!|v66?4>rqwU6ZK2c0w|PeMDN4ua%N)^u>Fu;H7m_>Il{_N z#ufoU5B`sZf=1PZ0-L z^82bh96{mQtT?1JfbXfFMwXnx8mpI4v*r7M(Vwxvdw3DbkbnVMEy`T#rk$#Cyzu~a z?yd;Q&pM=>nOZ?#JBSL_ti0aOUbf=sGsSB=RhITRuA$}YHe5kJ(P?`6t{35E9o_5Z zWMJ=Os9dzSzUb?(8{a*`$8yT5b`SOhcpn%bEdlJlZcDuCiE@`>mHL$d4fM_*E7zi% z#Mn%N8OMnh0~0gU4%EmUpnuj-fhu;PN8yfUXaOFp^gtI#buN(*TjhF$l|l=Xk_?o` z;d<&?0ahDSrzlIU|0t2$16mB$a&-o52x6T}PO;IjV*h;8&%c7B_41F-6 zkiP-hn_7U9cK?lD|7;or2glJCJlHmfVkOf_)ggntdoq ztqSOgtk=+OTscE(e^}O=W{-Q*<~2|m_A;GR7OE^S9c6`u`^ta$kH;|Q(dMh}1NBkx zWTX~{BvOr#$JM33Fk))a*E=WCSD-d>KZKA(w|`GN#}tz?owl5FbiL%KD(RDY(6)8b zSGvP9$CLs2hNS){(=AC~PQhD|w>$G4p8&yBpg;3^=g6N}NzEBx5C#^wpb2IkVuTf9m@Lq$SGp*?h~jFs4Q31kg-E;-TN3R0(5+ zurJDO=?-q`1w95`!e7_{iUf02xk=$f+oWo!aaceX4lnAfzC+TYlHE~VZ}}6L#j`Lt z{*%vOwo9fC$tDo zHwn__l-|)yOZ{SnWH;GMobeiZ>%A6N0AzVo{?N=f)Q$ixw$2JW&Rs z-ij@C$>3aa>GU>6fW zMb^wQu-t(3CDB&RCh&>#(M3*tE2$4b9Pc|rJw3P=d{7ACiW-Z!&|iW5M4ZT$VN&o5 zRmt5v%PlBZ%*x{EyamiHr6<}Wb5a$ZLW23E0o_5qnRTXgkZVF^wqB%eYJI>ysKdR& z-Vh~i5E%}Dq+y;V0Cp)Ycq`T8YT}h-dCIe1?pe%x8y^*#p7T9M-EtEeMQ zGm!#^+0;3^;9vnOnW?u7*!a{T^Sq_EiRJ)(N{3G>ElKmnLHWRnr=789V`Vq?37wO8 zttTktir`f-+dh|)qdvKelV93E;p~kOh@F)CF`!_evTCIEK-cGnb8QMA|1SexUQefc zI+TVs;%eu5P6-(p(_HDr>!F}X-XURSfG>}~Cz(p>0=nU*GDcP&=Bg8SKGj(;aumH5 zgls#8w|vX7rRt6YY#M~O5+P?kypenjYfcq(d60}^(|>w;X9Ul+Ec{ou3n6AZmXbVA z%Xl)&dGCJj^2hr}T~}9{=DDByzQ5aNSwtT~4>6Zw{{#pR=nQUGJ(3y!n8ICTGj=l( zLlHI8n`r>1KExDaO>HAok}k5kZbLuWG9l7on= z0AJF`6i!WY&*a%Ylu32}Rhav|n(F!!#;D!Lm5q5fSGN2ZcG^}v`2iObV5wwRAN~;!w}g3E&K(a zZ+r=Rgj+&Q9Jx7cmxOgY338?t(tWVk;C(!FpP4X$s}w6CwLtgiBAJlsCo7h3m-mK0 zat1&4$_T93D)>72 z2YiiUI!+;kidqk$Seck~q87zgdSn2nA+?0)ndB>UC8@f6Grg@42loNNa?ywlK|hcb z&*~+`wdzaW%5@-O7TN!DbhMyH)gN&Pwb~Iejek`#S70}H>=F{gOzDJXIr{luyCfLO zz($rke3OCiGX_q~1`kPJS&ymV)b{NH^?M7+*rR-uK0uag%eD_7Y={#KTMIAeO|?-^Xe?SC$z z_qy%f>$vvF(m%h51yl{@zY@ke>TqQWFhuVYI>=1k{L=b;13neuiXIDZ-7$09k5cr! z0Ie#^9rkXzYD{1dzecG~>wlo=!3WabI;{2j`?9ymO0$w*5%T%g-EurcRrr>Tk-O<+ zGrty)w*jG{dZBE0D-jrHf+=2AK3I0(oaq*H@@W`VvxkE*a3J9Y$eSd9&O4$^D%!{BdU?Rcb=lA=6ip_cSy_r8Rm?TrCpb zZjxt$;b40?;?b$U7QrNFW4=}uO|N+QWvR+vs$c*=F1MpLNsdy#M8Kz7oP|BoOb4K` zMNh;pHb@nSJBeZVH9q_}&I}pVJJs_q2f(81D?+?2s zbGg@c-oF)dcFE6qBfwhfgNOh(0gAlmVAgEs_1}_Q7QgH#$ZR5DJRb3uuCKI)Zx_M4 zl@G-F-OmesTqFri{6jGo39tXaImK)AS_4OGyiW(Mq-!0DmpeuF{8Hk zL)qQjBpjL=Ms2T0?hM@y7N)ew?g2BLgE-Bd72VIN?YA^fytTRb@Rn-r`?+Va>Crf^ z7qL|>mEHBli`#cTzW-!SE0%YrUXs%pqq_Sb0w8~$AEGwCdi4^Vmv-UQw<*5gao^(= zEikDj=iPyj%*oxmUms}*EGv$sPt(PTpjvJIV&_dnGlS71ihOh$J`BilLgc^?&5m#? zOP}#bh)G%89ZuB69vrZ!Wc8>Zb(T?oK|QIp-D>f)HM4Rn=-{~zf(`N>15yncWPB@D z%UZy_@aexLdVhwT)aAgnKw19YVMS$%6wJD~IAzd@(n{rogC7m2>@ohUuXYGD=#$-M zl!S&+erZUs1?b*ZAu&EDaF$Rg&Q9pAD84AmWUM45vYA>yr^QI=MPwT$CzmQ~9VUm(+MlnqNlP=X6jW|Q%PEuRb|ktg{(q0>N-U_z={l}mhL`hwdJQ=8jlOC^RLZb$KJo0CcDn@HwIzOEeiBCHm~6(*W)M?lJH%x& zrd3fRx49#a?ovlg*cbm8Iw6Po3MQuR_ zSt-us%8?i1yRv$mF%l)jWCHRjPyp{q@f&VSv)L^}cktFx9KtFw?9WoWa+0&3{`;n) zaDCpKo`9e>qd!@f0K1r_NX@|VU(#+ZTTr`!R9?Btf(`g)(sgsSB%$;;`U%UD`;lU2OwJK_ENkBofLkNOvFry9-& zB0RR5e&>_>S0DE@WTa1+{0ccSGbT_IREb-m+v#PbY9S^^YWuwF1+p5w z(yo0TMFwJ!Y$t&C(XuVW;`E?~?K3V5YTC`yY&x_LKN>951GN!#Ohqh5m=Y(%&JC`6 zBo5pO8gy)@!5D(3Swgc*#olsUIqXNsys|Rwp9f0qY&i-(Ex5NUqZ-G1E(cweHVyn4 zR04A?cE_^-%KM!BH2!TL z+8+daxn+pkW9h-+QT(cKO>|m`FlkgL<)JaFQt(->1?bXlYKfS{GkI;^lmDW7eZI<| zoF0C-)C=OZp+op?SBx z{R|kQB<><<8GK;qg%LF$K~qJW=pdk{4j%!hoBhY-8ckgrBXM%L4gj^R(tnH*8@7qE{CwaUs#5v`A7q-OQ_rR^e->1zeA)mW#CgV*iyWb6a#nK+JE)Z5gY>}EW z5Ot9~xom0#GV)`>b^CG?Gw<^!3C7XW5#JlFfl;WU&1?LF2n+eDJ>|%>Kt|(GfzXF| zQT7lY*0U6e6Qop%sH@1Du;Bumt{U2S;0l*@=U2LPW6WHz$T{n@gGPk${_R|#NJO&s zj?lnzoPONK42DM`rYNaqCEq9MXNXi&vO#Ll^tv7%Zga=+jw8u}TS#~1$Iwg|!y3i< z#b%e2{pcM*8zJc_)#&3N3yqp;TW?K9&?b`>{{qUZI%2<3>~_J@N9b!S(Q`zE@axA5 z>Nz`B;IyxUj?`j5TFkh}{D_BcCK=*J?)^lk=Ysn<9jE zdcirn&iiO=Ig*)gxz)Ou^;2tFuhil4+7Qn?kAU8bS<^c2J`_YS3$US2KYqs7@!GR3 zTxt{DU-tiuQvG=R^U&?k(#Jg$Vb1%uV?V4AkzCjTc^=G9HKQw5&%-Z7< zFP`b?CLRyHM`!#OqN!4k7?XvY)0^wB%0BNLUu2Y&kXE4(SDek?_(ANOngN(4SQ@K(?>|KZCza_-M{7=DW3Ont-pFamyZ3nZ>N^u-`8vfde9F?2>u>FDq zFZ)IjpDDZ9;{QiFmGk^}756BT%TthR?89#6vx>G-t6TfOaoO${EoP^p^vDi!7N!WD z8fuh#xc5nA#y4_;mOf!^ZWsPBsptPZS-UYFeAls07wSWvUGAPj3NL2_io0eH+snZXk(pX43OlPQ2N8AR% z|IOMBpH`4AR9YiJHcTWamBPsi5%Z>wiZKL8AaUjaqNC9%@9IsK5x?^%Sew?t%Z!7a>%^T)R zP1-~UC+MhSqe$gd&$|3IaB*7yGWO)wMbDe-1$woeCeK~1 zL!TCtN*+%vq8o@xBeWD-vN<>hd2rtS(pJ1KIX0R$Fz`cb^8Gpr_shO-o)TNDgU4Hm zNG;0@`-;ma3^C2+OUUOzaq($F-lrX;4m*F1k?j0TuR7bq29iq9KT@C{HkDUG%4s98 z|65}CAW*a)uH;ZQ+}>|B&V)nUbYc%ZJFgZleuNlg!Y!nf4?*~I%W=WkR_ zG;MXrNX(zWWXuC#|WBDEE>iUFcv;fSCz_jdFW%7g?aviq*Z^gmXo8a_v>}GxDn?H)I_?Pu*`0!QuGf`lt z7~sqinsY6*Gl^{b;uB)bDW{ro0*`P6<%)iAYMO1O2HD<>HDUa{qt@hf7woGo-w3PC zbVc6wIPCCb?AEP;><42l`Tkbr-Zp|@-L0qDR+xP3$X+9wwA8)FBX=# zF#;hGuYBe<(0?gBmt&r>_3-s4A-x;TKi%0SRjmO#%apX6i&iD?k=RQwz^OS5s~+gk zXZcP*q*pkCu*yS>%%S1r=17J;BtXtLieE#)oX`qVILCVC70}h1Ko_*18YSo$2RO~0 zIR&*`-3;XchM@qDqP%6+#C^40QP@-=U5C)%pxNW_t&UQ-=NQzJRc5Oi+>Tbp{&nvh zz;-U9yl1TKmF^W3XFB#4<%14@$|q9^H3D(t%nHW{wMuMJ@pNST_vpY05a&#vj1kvF zj1K#Sz?bnW1!k)R4TZ&*UhLa()7sXZu<_#4mNNU<*IP1FJNxfPnZ+NY%;P`*x1^Pt z#!!Ga6^JmZNN@)bDA?2_z@*dHDrVRiDzg2YN?(xtCBee^oNqRy+BuBtJr|qHXVdj= z20XclADO!BcIWPFdzliC9j&Pg6|-rpavV=}UpktUDgu^z0MNce(DN0I%4~LlocF_OuV2gpX&9;P5Qef* zdUbHpZZtT7v1#@d8=lcuY$K|YJbe2D*Y!jQH33B_JZR%KesOhG~pfV^H$gHs2lYLu1E{Cj9_G#83=mrEP8Yt{$=$#rkBQl`T9lUdnn%trUp;YmoZL`#^_Yy2{6is>WzCYOHU~V<^_B8hIHie{+pvM~$Jx zz6eoraE7VIFr)M`6+xitVxd=(!-adI_rlZcs8z_mcz3goAZut@h+W)fp=wy~(;tHF zkiW|cgSbAUv${YQ8{8NPmnp#GgR&!pry<8sfXd{r9vYC~&Jw! zTG))$xbn4y)RKyF5p;*M@~mo&Qbh)#SWC0ylPRAa`zVVCsLDI=nv^}q$u6>D z#&UU&J~2yfLdL^R*+Rp3FWD1@v2+V=H$kn9zen+)`{@9PmqIpQlvfLS)VN@VDpkF} z09+3HjI!Llg81-bKPDV#I}Tr(HzJ14H+lNRO&zvm75R90NBP8Ec`S7}(u#EoX2%^n zSIvvw*u3N4Qim=164%4YuU|j#J^F0?Ns;cy#nYncxdjPY4nAJn;|EX_9v1W{fNbbJ z5(l-BRYF6bl2{%0D$@l-9kNf%;LiuBR-mYGDEm>Dtlq#b&1rqaJG1<-BHMH(kkwBw1C@ATpk*YQpBhDbNZW*oa5y~nu3c< zPnz96?dwkR0r4k^UKK~fo-C4{MR-uOcn{G3jankzhPd#FOTcb)W`_d>H#63vAHoyX zr%LxkJP9A2W>sp<`2DD8v`zDv4myQ3ULO6{EXP+$*!BypMv4~ru+d381G&)*Q;B(I zUXQ$@W-xiGo_}>m$Cco8xyHo2 zg;PHQn6pGL`G-_Hv)vh!7bo2c?lOX7E{zY}e~$Ylp1f_Ga2svmCOdOE>&ztk{y=Au zo`)Cae9D$Bp%PO)`}ZC-pJbdBI+zE{6NE9EI1D7*%n;(}ZWhB~Pzo@Z0@XooIyUBc z2xSSJ`DN#o!^!+Nrruu2 z%ziMbJ2QUk){|czQ+>X+F=sQnrMr%kxvB{{RFDAdx`#@c#D23P`XoMqx8M6We^0QM zmq%`Hww?F=C&l^~J;nH?)V2WTDdK18-LW?vb-j40kn*0m2m=AH<$0YV|obKs zWZzP__{yg1l6{m;FoeQ~FqO&X$R1GWn;?5dAiW|x(b{zy^I3FdWQRa}gB&xb*}+&z zb94{_#33^sFEApge`(ZqWF~jllhqb;7~zJ0Xqi&yTU7YmkWT2Yyqb)Q5)q80GpW>pGdq7nmxN)m)8riyk@dJD)SW z%VZM=B{&!RXczauV+6`@JS&52+b-O3$QzyVC0K7?k-pOOZ|N<+U8(Pj0|~~-=B{6k z{F_wj>zWt&xki2gU`F_*jkK@(B?mtsHBYkn56j<5G(VGoknuG|_XU3MmF?!NH*iPeU2Dh3-zQ!$Vw7l*UZy zi$rMA@-=ZTd4gsVCK!%{_!k&TvX;t7xsvpV3;?GgP)V32MKY}F3WON7G13#DG`zV; zY7ZSw8=WB_PAi&1&i;%r*g)(y^Y`^tP8shbW_(|b+q5h6)N~v{cA_TaL+qm`#VVk0 z@;i@d5gK^0=IccP(sgZSzg4@C6r@%`aHWiQ;d>(JE**4JmwkmdAE|WRTYEoXDCa`# z&1SraHa8|YK*oYdb%~@X3QlI8Kxgwb<}FV? zcW%FkK%;}QJ|we6fnPq6Do|B%MP8Ru+da#XiGOQl+-yR&G`c+0K`n=_HW}#Y3srp) zY9}f!FSWXsYm*Y{g0+zREe%AvL@pGqSz?t>25xQGJz@^dAmEFBYF4w4XjR$PW^k|- z^H@7ayX@L#Jb^7X>Z#EFX^v^A+gN>Ck#C&HEwjq63sb-ubhB@6p!^Ij*)mw)8D3X2 z9kzM=;!N(;e@g;)c^&CGn<{AZgV&Ylxl9k0y1d=ttB{#*QKHLf8=Ul;f3f_TpdBlNhSx!$`D%w-$uWM;YJ$TjKQ+6vKdI5?(%|#J{TdGccO% zS}ynnZv=p{q$2qmm|vr;P8#>&WEA~07Mmk7#uE6m6c#aB|cIN zq(hTy04bB7et24prP+bHp%~^@nKOr?ZL}>1Ge<+A)JfS+b$4m|W8ptSyTYF?g?~M} zmd0%WC<$=Qg@oY4{dOU|P-!?(@dAJS7hs@Q3#Q?P zM5(8&UX3aEu~q{p%B_FpN7D#cDqMixG~5*+x1X9>6`oFgOL~k0RP5gk7hStuw?`g5 zO1NPgE9@V%*nQ&;DUWrh(KqSu`xw?`o9fN+xO3GbuJ`F%HQ#*rV)Nj!$adGUh;IY# z&9o(dP>>020digt7UE{%&r+`~QolB#)1|6KEO=87+s_!8in<;yqGrG|L0j?<5=XT| zeFCqVNxX_LL1EqE>=L>9V}%2oDSc>NP`XtGDZ{akisdX`6|%@tgG*mYgcvUdw)1_L*;JyOguKBeQ#6Z2fk(!ujFD_%AulS0fo64ohZ{G<&!^UwM%6;L&;ZdzUNRDmU!(DHM}Q_sO}kvY==F% z1Z19a4R2MOV2_G${<_rDj@_bdsN*wqrjG%THLR+%Uou@Kjpm?Ur|OMQ(Me;Y(@2() zZ$>+WS10Zw+`t`l)$-0Xj`%>^I^c2m?PAU3nZ8?SUF%Oxdbhh2SbEE&1By1@3VjAi zr}b~#_sHuxUHIXFQE<%H55~F`!MivvPvx~py2M=mk*YyGiyrahN3rL}>^!01IUNBn zGvXN_&n~nZ+HtN>9tkt|pae87`YTnfYtwZgl0N#a9^1IK5iJGE>j3s8x1#TDd`(q$ z2Eek-_V@%ebQqHcG+N{<Mg{p;DPB#h<;EFb}b?ClTr11>{c-h%1nk4M9ROx@U7J|P{&UlW6G1UqyRsfmY-_}{VYI>-+cSLzDq{?F z91;5`3gvBOvjBl#0xTn{+-T3!F0LtK>BYzl&CXE!-BhWq`60e6#&Pu6wvuZV1(X1&%ap$V(9d zT<_7F$H@eupg|}Yj2T#Hb$sfnVcQOxA5ivQkn@NrlEvPrmB=$>uQy&NeN$fBcFwxo zO4_me)7%-N|=ItAb znTs`4V1q{AYPk+kVJ2H}DjywIPgM)q$4MYXYDIMd3XeDnyyV+qRHbG!$Wwf|4%tv7 z=eR+cE=V?2NKi*^h8daF53Sh~#~mG=oeqQqumM5VSSn05(t$|Oqxzv{OMbxR?Idmo z@Lre$^n~T(NVf=+gjb}jAumT5^jFiFwP+2~)J$L-J|N_F;5AU|zT7ioVbl%pJyJ(- zfjWB&SfiM1;}CZ4ruGWLU8HC%xN|JLWg~7Rkx21kTLpD{J z7WN@y*?Ci$?oS&oth>s+^UiImh;4m&kD0+>kHJE?dAzHy;iuQ7^}0WvelB&g8289X z7tgo$jGY!>=Ir1vz(r(xfAhHkl8S)I{#6L(Red#qyAKU7BUKAbLKe4LR=NmHOgkNk zYeq>#Q$NBM1$`h~s3r6x;Ahp#eze z^Z-BYyxasa69&EJ&xI2|Tq5d|Htjj>JG5a40MUC`KQ{N-b~alxjNk)C+w1OGUL+Xq z1ul9Q_`FpC&e38p^h*c>#mdc^p3x$Ha0+&_8GC-Yyzd@5(S{>%Y3Dj=Zs8SyBT<`Rm@NYIMK;Tkd#x z?GNeJ;1!#0M{lWmTD;1>{bO?rtK8B+izI9*8eZrlV)FNUVKUhI_F`irJ;Y|*`cte zs0a|AP|66$=mSdk6G?QC);6&Nuh#4?us;c7lBIhqTp1?Nrvb<2-!G9)tH;@N&{`eY zFISR4bg>sQ24jRJUl$m=4)iu10CO0QtuN5Tz{NK>e;0Du>@it^T{i65gpJ zI|Hw5S@-3^=96Jte(!XYrQ19Gcq%%ad2@5uZH+8JTKSfn(`Mf~9o=qBN{^Sa&! zE>b3}8z*TtXP7T1jeISN&FywGCtVjQXF)X#^a~_AkYIlBb--&($JZ=yq7rxp3>~Qx zu#I{U&CxGm&8IiT)I~)7HN3J~Rx6mhvT9f`or2x*!YuPiL5Gng)ZI>UcI_pfDpd0) zYLZOG$=#A5>EB2jsL}>`UlYJY@+tm2K{rf0mo)2&G{u}0xtmnbMKX*1dCpK=`-fai zMs@h8Vv0$hAFz!{;U~5ycF2vR6arXU+`Ia6J`a#OKx(Z4g$te2@#ioj_3WT+ecZ2$ zAkoPehFt1)6I$2Yn`g-^_9_JA@xl5Yh|P>kJv}1q#C1nvE0NM8vciMZumZX+o`bqR zE3`zh{30LQD&ealY-n1ujpAF+?8M&^qRVHkrMsc(kR@>H8V5TsGttAMao?xyDVHd?;{_V+S@b2GBzJTqu`ZWHe-jnF9r<tAY{tb zO`#v#P!byUY@sxmoc&tv%PNwPs(LP!vGZBby=5cHDYd z4KQ4eN{J(C;Ea%A&gjk}!Byi!!#HDdfc}>m;CzxSK(C&s{nbZaH)AP1b*y=n-=QbH z!(R$ZO34KD+UB+YGrHz_;ni?pbWK2f|G-E{1Bz)BO>nHL!>Ca4JMIG4*&-{7wJstc zX&?pSBIN>_dKq=B@jxDJt@%H=CgFzVCNDiF!QervIUvsO z`%2j$CCMtV;Sf!jno4$&?BECkH{zGg8f|$A8)d?7d!dTF=ZIa0kq_`dm2U365%JT) zJ4M90IEqxPKS%3qh{(qX>Eq~?VR%g+HdO~pSCsB4jD~gG{CjH1M~A6>F(@EvsQh-L zu6(tTwr255MT+mOR&QmCo|G3bFwQ;kr@P*hga_`jbSdo*TBcS2&@#XUa$NS(27`EC zKU_)A2R@=}a+5BI-(`7uX<|^xSF9&CA0! z6{A5580)iEsJGzuIoI^w&HUnprr5j(1$i#dUhmPQxLk6l903jy2Wi=Wp#9@9rJq=X zpX(IPkM6ltnZ;8rE0?1YUh6%^i<8m(bjr3FeM0KbH@5CX;W~y<@5X%A;&?v@MhrdZ z0B+6gSzmY~H*{Nri$yO!l8Bfj@oF%0ZcvY?KA9R?X+XB9gVE_&4e%zU+d&s1?l??K z{&tC}0Bo%EJL-#P%INCN>%r%OoMwJ`NRVL*l%?(_@V`vtP9{nbg4SUZf3J^F8nxhg{+BtEp%%@Q!^R;r-2~&nI_-JtrPf9+=ZRR zrQ}1PP+1iRQLm{zz)TL(f!PBMmgpL!dr=8a?X@aIE)>m$?yJ=GP%>Az|sLc;xYQlYN$ zkf06vQz>pC&vznMgd@k+%)Ed5c8`jfDodfB1<^fG?1$8slTpZt36&4>ozi9CX+9IO z0A1r6Qt3fV@f*@buOk(4=8LxAUYG6cQCqc7_AuX9+Q7L4YChG4oULRX5e;#~+6nf= z;*okdRrRNET)z*|HOlL4Tfq5dtOk(q;6oKe|$T&-By75iblY~ z8cU9Wpc^C~l6Rq1?XpjV5TUYm>Ujmwtvdns!!b{UGTd~%2ivv%COlAh;&-+0^K8e4 zeI9ig7re6`7uMz0_Fow>#D9EEl$v3_&iI=L)Qsob=xjOMJsUelnX4HIugpSD%avXM z%0V-x7Lu5r<@ zypt4JuAhE(X+d0dIKGPZ6<`B1n95QhjXBcA$ET9RpOXUzYg>q1eRMlIWicu~aUf1vi^^NLbjZK7No(~iBG zC!{o8Vf}%P74waY=@U}j6?+T1ObQjd-Vb{pfNvMWSTMcOXn<&}+4$?Q&N1zMJYi0w z`_QBRLC}}x+F_uR4ciTm-XS-ff4%zMpw|xU+Zr#j(^(Ji0EWz^`}>H6{o%u|yGJ*F z=hUC67&D*L^VTcFp)C`9rYT>K9jl;Q6P2rHhg)uReW6xYw$jq@HNfO29(4MzgL@cA zw?wG4Qdj-5M0FHeAQ$+|HKa;bW5O&``zA3|U)=O4fRwubmX34(J{zCye3q_b`$5(? zR&i}G2d!`Y`~nE-3KtWv0e9d{yYv=tqS597*AI+87=O4X{m;DxbVZ8*T*&h>NLp3! zM189GB&^7xe|>BVuwsu-UIBrl#Ype&f`Us~7&JZ2WxcXTbl9 zD0Jl!)J z(jqeGeEQR)xuWH>g&*N6vHaJb{Q~P}P)FIaYl>`oR)emtF2WJ@;@2-(Tw?L=za>+E zO#E$xx{&Wdw)wy?-!B+H`0>w8`T-3{I99nq*|o2f_9KzmEd=_~cNgta z6;(T((OEY>)-+z)Q+=)>nqTG?V zn);Dh1av@)Eq<`XJO<2JNRX-mpd&FAYxc+q>s(q8{y+hkGNv0U^%1r44YOO6%m`L_3W2 z?Sg>sj3$~`MML($;)iWlw;CmItK8~9LDY+|Pu$M~*S}wW-}L&+lJl|HZHi-zJ<0gp zZK~Jq@ON(e>*xA}9kR=niA=!X9tjqLr(Rk{+U)^coxG*avcfsCZ3%JY%N1gxbNX$J zCblxI@)r?t25Yjt;45O}vgP}U?Eb{$K)gPsi>^iXl-Q8m__4PnN2N{>lP0ybDzwGZ z9FD--;QS0vcm38}Lk~wJ6`dYvb!KqmWhqbpuwUS2)SV3>{kZ3M8CSRbexM6YnQpDx zeqU$kVoLRpK&R8~UfjchSyPoghBfI`>{DLn!!E}Mq?>o$yXR5m?P69Gxe(zPG8W1G zwB&?YenO%Ne_C(${=afVuIOOFMR|~dQG;1di~TO6mR&{&`QcU<{Ld%k>K6%>^XhKlBaIF?u5kzS&Fjky{V z*6cX6pkze5pq!f#JL|LD?Imqn$^8o}sycxzh3(wE*&vnOom7EZXMApyr_bXj zzTJBif1V)Gktj6hdNd{TZ{4men0_1|(#Y(ESSCs$vxc#rlH~(gr*n@PIXn~Nk`lR} ziTMzP#jC;M(LJ0Wl1oy*0&qs5dKw#rc(wy_FO)@71_8`p20HV~{7R|SSug+<>-OP= z`R7Udzk;-U*|c0E+U%RfTp&i!lMW(kR-rL+1H+C0yQKZz@HMHez#o&`ttAB#Z}Jz9 z!-F;q*%EcwiNyHJ(!D}b%*U-y&p;p6a3dc6l^xsfdi>Xk9b4zuJb4wcJMHb72&?nn zUPoQKgbQIc^AR>vaU=QrHPYGC<`V&yxWHpU^!N88%ln?r*WGSmIQLTdMZDEm7kK|( z$cxURQ}!IQZ)zA3qu$08O)wQo zVYm=@7j1x;30O~2_A;t6g6~+&>WSR+6I|*daWC)})FT-?0*DWDx%4FbfR-i=6!a(; zSGE(E*kut?1;f~A0ds{}Ha4_!cGzeglzLr?d5ENTjIOTNG1>$tzX){PmDyHD+0>X2 zfAaSuK5v95ep)p^St{?iQquY$*cI`I6y?fGh1Aivv*I#p8hTYNCUpXfI6k5DqScw9 zw524BGZe#Q8^a7hzBh->vTQJ`khx|+O>Hk+o#GB0tE~y^kP5eF+Ospj6Qxb|lwwa| z5AoC(>nLpAI_XiEAe^e^?>X%cd8flDv3(9c$yq??pe{kaT*Yu57TBm!yS53VVL;|a zp}tJFsaE{;;zC+|%bTZzO=sgTj!Y0Fj(cPuYU(O0nxeFR zk*xztBS+)Q-j6b4EU4#9pU+0vEyjuHa|K0YW8~Fi@=^%j#>)XDSZ+&H8&{a!(~iI9 zjaIF&#jld?au1mCmhXUvvw_0iK?J;@R)ztLNjotJ+$?sH9Yx^)T-HuiBR#?@3EcNK z+md>(RJIanSM0bT^5z%f`=o}jzo2^}TL=!|ZVnR#6s=ZHw-Gh4rr^d}g}+r9a^?N) zo;X4GMVlLcWpa#G!Ooe@O2UKZba3i$^1qiaA^KvTxC}Wh@e9Y-4TE5Gnx6ir0d631 zp}Z%q21o#ZS>bv4M)^D3I$1GJku=PA&XQZqQdSImNsmhQ`pLT)yL-u@kI4SRkKju{ zkniy+1~BzSL+{|d!p?i);lyT!LO8ufCsV#I%*a^w5O80D1(+<`mT1rkixU(ki_5GvU|;xqM=R2RI82G!#95BTv`n&Zc;tjxbMl-l#eO7k+}SI( zrrz>bD7T7{vrwD#ljB$t?`juYHFY__fY5b!touv-YhbNKL@az*^G1jc$W=P#*U7t> zwUw>Rks?vVbg~T{WG}#E)Nr@>;vi1_fiw5YmzhFEDjL&TIf@a^vwM>6fu4;E=>ETv zdcmHp^3{<0gQ-eGW)23sJxm^P)jv)WP3e-agR<+hGTVe`D2RLap2R;dt z?;NB6mk&jJ2$14iE8jr+!7(xzI1BL-ECFlau+=E!gDvx zQAu@ua5yFP8vsR|Uo~%)m>?py9Tu%XxZyQ9BW3Yi*Y51dJwbTdTkpy@qF&XG)^wN+dJ+N!66`7u$Hnt=thhZXz5 zLd6TT^Dl^Kqh{i_vTTaE6y5N0SZ;Xyi_vxn)rL`|uwxg8T4YUSNODdpI+5%Sv4nRa zYD%-m#2VP^UI;w|qTXa!C0rW4v|;PMA~STX)!+Hn_14|rAJkUY+IwBOdUK-he3v}X zanrBDF~8qllJd1RS0c-q)_DK(%rMkQ{!!$08F^_xKq}C$kJ7n;%ldzKz^JxzJK;2I z+)X%ZG;7}w4`68A4m5Hzgr%(a>F@zi>D_gGo_;cL~Tq6bKdW9RL0L0Mb zo45kNj04yMdMcI*O{vgOvq6T42qdUR79f`s0UMihvvT!$Jx5_a>Gv~l8MmJOwtqUb z_^e4GK~@=CSQ_nb(MP;%`@;Kd>>x?-A>iht_@`x+1!a$3NH>ELy8T|ntd7z2H^nsS zdBTW6yBMuzi&-llL|4;OYUZ=7Zz0YxLR1!j*#CTiPJb6;3WjmQ>-j#gZ6j$;ClDX( z+q`9<^p44H*3&bKq@9Nn>lJ^YpG`3{F(aB{BiYktg2W0~!_FsT?IbQpr9g2G%vhFB zAloIlAjC)P2ir+Cdt8AZmf1d-%6&TAYydm^${08Uq=}<%*GAAL#R=ah3JEJnMSPUh zRK7L6C!-|4b^c$HsfoOWqCqlb7?N#}%h2}-XTKr5KnZeAY1VJ7W0pOjvK2W0}>8E*a1#O{Nm^K3Ukl-kwDKDUML4sQ<5I6PQEK1&b81XE+hJhw~V25Zi^ zR!TCehn*)Qz2c+dN(7C2C>iEu{Z(#Nulm;>b=-zGq4Fyj#xJXpr67v>$(UcW(J7Q| zzzTOI5+@{T$Uhk=43k48J4v1{z{+8vqc@zf@dH2$?-1C?SHN3l3s?4?fXvdrVUKR< z7L5~Fl-2(6m$AM&S>z4BuS?LQ)Ji0dYn^>%Ob}c0 zkkrJw_uLIuIRx1yHxq>b7~sa1t3zf9znGu}+OgfVKB<2{(38>VVH33~YooG(XXOJC zkhz?&8v=HR3*rkcFea&aKNy{Oii40SC|so7z|*y%WfE1es{DDBR10wyJcwuGv|yto zaHeSN`PE&*H{>WjPhDzN;}Stjew@eY%YPx>{qn z@<+!;#)7h&nlQY3=8wycj$FaKF)xd7)A&3*(j^PkycserZOdFljAR%f*fsn6PgE>n=o%Bm#VQ81Lou z|KjY;)rbvpIBuQej3?*hT zmP!&rh}kNHoF^G4X6AYh-{1Xv-OuZJ{<-hpUqvrD<~)z%b9|Qf(#}k!zM@Rg9}6!c zt?)4T4_kmH6Tdy*TJ#LZ0y#xgJA07Tj@?{;nm3a~Mb%63m-z)E)r&A%|# zE{i?ZPxeDo$eZbggM1*p07P^*^7%6Y!zA$z;w}J|0|*XpWKOKGHMkYCKCbl+qIADo z-!M4g{%*5s{araRhErLgAv93oz^+ac(l$dHc@e^x2)3|mqR4H7@#p6!yGZtuHWito z;5jl+2Mdj$}cB{oo zNNSsgc6J+6Ug*JbMg@@8!|jO?`ndLdGg$f!+@4f#h)oAMi#`vphCCZ5+xi;#3}lq)^pRcvf>a7_Zrph7j-riu%4JB zqUTINE3NP?Tyd#L)3(8K&kI$%#2Q$MfuF7I)4;Nd&t<07ZzIW#kk1qD37y6Lu-r6W zZ58~fil-mtqXpc2;y||d0G2Fy_g+mw>apf?KbT(l3V&-Ak8E|qpY}-a|eczXB zE$W71cuiXKTl~}aUAJ3|QS7Re-E)BTfDuLEbj~{#U|Q{EZErW-Ez1 zaqFf?x`a6HCXKD|+Ekvt17>TW=X>~`58p)E|5?ejhQc~_n#I^iaqF+Q<-0jm z+G^YwJ>9nKO*Q~b%!(LDb)GUTC5VN7bOC@5*r>`x+Y%(3g;9i^Mqf##)JK(1dakWw z4j*QL!e6s+`N|=XntdfW5z!o)8X1rHH@=20!%@Or{{87zQcZ7;AT;&tm%|p1ex0}0 zV};S@es6pJ&2@Je*f(Dn(N?9Wo~7n_m`9OQKDoxUKC;h3{F|L#^7mJkxlSzamTrCj z#wUEpJUpbRaNrE+5rzZ{%z;}fkZN_iWlBwA*Wz2eXbG0=CY%IX4&($!thW~h6V20z zSAhJ!cq7O95JO#K9cDe$p7NF4tjKAIVPW>e3&OvB6`(*kaVl{NgrU`!ff_98CP-oe zXv4|@*Z|zn0uBaRSKE->wpz?K*aK=y=26!Q9pFthvw7;%8uHLs`uscM;SlpAcB_Ui zWUSTOC6U@x_I*S}KpG+z^iITMTOp8bpP1nPrpa7~nc~-KvEt zOEfB*&Cx4Q0oh3QCUaCu>bGZDTBE9Hyf<2p{3oAI0XjUyd<;Iu&oijkCcwi ze%Qa^!lpNysPu(GD_fn8al_bcWEKkf!|@l(gLckg3)RM+$jY_XI2)b$I4`0HAaCcb z7$5D_=5*nKU-MtI59q&yAuufwvo07UVb!!m70-bLx(&MB)tsS^9!8J*d3j`exxU+f zX71_P*uCxh+YSm)Dl4txO_81ZMn5cYt`-+H1@oN$3bvavtCk7M(N3#2lpnvQiv`{- zME50QRiDV};-Z==1wQ*PWVIyvVw=XiBEyl>d+ErWe?;a>>~8N@Eb zmDuIF61(7_+!y5c=OAZbypzmc;e69>M9vJo<)s(BKz51oH z_r<|Tp7YkpLsK=?$A0Co)97sZ$sBN%rC%oQTKYgZpD!aXt)EQx~H}8oowtb{Qm*PoeO z4e{Wa&(>y#ZcBVU@CKio;Y658!~y{IYEd!Kkf&~V1A`)`V75bTsa#sBvM;*F1aQq& zzs%Et0;SNVd^~42fVK-Q8o^_wwFEv!&Jq?+FphC5c0S8p2%SIn!EIM{=kXv0;Xt)Emy+DDxrZ78W{%9g5>Kr;8u^674|LCkVZe zb#+Ql6FS`CK;)jua9?)>MZWS$UKA%g4r1tR(4?UEx_DQFo)~LG5BrH_0;ph21xjZ} z(mUK5JV_n%hLfjneei{C*};+^?1n!fc-%m}2=s>(_icYVB~q57eCeNfnGp7q@;9=1hh zxj`qy;a4l8=>JMQ#jbvMx|OQBIxJBN*aUjoaW)9IA}20iX(w_PY_45kuP;uvHhrnN zuLQjjvrqn`T|ZD8XyRA39ck>Cd}Hv3fyGHbP&;xB!r$|u39TP;L1Cdmu#;YKG-=l9X-%jR{f}Ffb)C2Q{iYue zZg(U@6$$wLx)$c19$MSC+B`M1Q^`G{ySvi6#4*1oko0YG)EuG!FcmH#-v};Ftv3Z| zJVH>1AfOLWftZSumWBz|rUilhqo#;&Wze;ns-ARe6ViMILbZquf*T*$HA8b zY_RiSEw66Bfg$%ex5!f+NUw+9*L%fE7_fBHpHs zY@aXl>~RX_ncrq|{Q}j@V};R=$4OHCkzFY;A-83Wl?<8&HE${%%n@6e*BiMNzPmJ& zD=+N{AH(f)UiLaVJ4CEZInn*y$#!ID;P%TrdRCVuog~@(`D5SlUQF4;-S^)q?RNY+ z@U+-5=S=jAV_{iGci%l3`#d-7XSa#?r6>!4?5ok0Gt(8z7YJS)^CsFlVMsOXL}~6y ztu+c38$q@4Ca4X#BaxK9f^46-GqwSjZ{@iM^{OwEdVQeC8t=2`z3)t@2sdR4N-2VF z*t|IgkL>bqkD*Bcz#dRJwDKl;tKbyv6ZJ`34#P3+z8g~olqOL=Dp9?0-}p5~1y3-- zSAkMJ1DrO)udk{cs%1G3YlEf_A6yMM7Vg!>3MiMQp$;Y?>GkHo`1~+1{6Mj;=D5;d z5ER||={_oU-C}jbixs>PNwod8QH8QExXpgg#W%*|NSB~nRH=#dmY@=FRZ91kh>Wd} zGKJ^GR0?3|eIbtM9qOZxoS#%P6gm@k{wQawO_JeB@WfAky4mzsJp%Wis_c8fy(~PK zdpQ4t3qr+^8LSsZ?umIHTqfrnb2t7v zbpOP+GMysl!4DaNz?ff756E;>t0A(-?B?-)R8)&uC*evz{xC9j-ybP;w zpVh*O0$ouF(5AAoheqRgj;K`j24bLMkXUuVggq5+`K`{3oiJ*s_Ok(O>$r7z-F5%gNj{z2Y0FOX1yj#QaeA z+UliGWl(R6czYxU-CcdrXz|dFDfexre9zA8Hp)h#3bj$Z4sNFiSEcq|=QEWFn#=yn zX3NLLEqdkSLjyuhF!4%y#6Pl)pHKYT(v?2<&F9QRN2%s-_QdS0Hlw&(7B@TG+h*@Q z?Fk(qR@S6hZM7iF!xqP)!y7Mmp=^IBgg2Z(pDu0L_v5x=ZkM*jhhs+Gqo4N1QjKqg zexvrYYYeBh!YYC$;G21omO!p9Zts#GCmBL$3JK^;ZJ|@+H>zJp^GH=Me=T8e)E~+@ zCW~$oE4#n^J^#=Yy!uI?`QXM2(D(dH6S@>~+0#-@Td1fl*e%K@`{5+Tt)oU?gwC*} zz_lJ3C`Fdr)r#9zV5n=pM-5?b34{6X&~Ct`Ly>#j6BEy6$|HJ*-yC|Lf2z6LyCPoA zebLFS4O=a{Fx87~q$p~)EYd^l-Fj^Y+SG6IH^G(Ja7`RfR?%LX#*H#YBX%<6W?a6r zQztbSi2UI=3oL3JFnwBwusvQOd5;OGR)!7yuS9#QPYf^rd8Ccha!ga*@A&e7rJu@jAU-p=F!xZih&%V`oHs&$B6b5{rAnUct63 zuNMA={J=CUYXo9Z)AM8%;vSeL+;iQr8LLERr>JF3r^oZqw_xOYB!KB^AoS`7wT>nF zZ`|)3|8V%Ydu2WgH2%9F3ZFa;6k%5r438l3_~%YsHi{5By5I|qQn=H5erZVMwl4{2 z8||@4xh=mv^&=Hh5zkHb2j6_WkRm=Oeuu3kPi0<*v)w_Qm`a1hR_r7-H&(;pW3YC7 zH4sG+%?OQ@ncTtC?%M4nmD?du?zU5Z{m8^!*yGR{p$AIGOh^cC^m1ZGHEF9zVeI~kyR1go^?)CvvA z$)5J$;+N=uL*L6cn?iire&p(ABoIkQCXe zPKtw^Tg6-axPC8R9QSHF|E@Il@Oi4G=p{1*3`*0pIkc6Q`N0pfeR%bvhKW9jwyFSa7T1OJtPB zHoqeB#RR-6O<<;Y1ezm+YKm@(QnZqaQ_VVlDyPO6Gt)_5sH#}bGwMd*KC_R-0r_D( z5K~QLW%sLqW(*#0EfflP7MxC-+FFDlJKti z7I6-KHUQ??aH8NUBz2RdLv*i($kR4O2-WS5@EkOjkeFasz%3z$j_&BLPE%a*Tb3mN zEKy6o@@I8hsJ8+ld3>$fNc1HJpGkz+s4ZTdM&BY9jp^dD6EaC#k;qbAf4F$fg{U&V z_VsfaKYOk1jFtA-UFf(Urg!QVzT@}lkmtXiRHp=WRl0TSY0mRGsAlTWkh(XxY3>9ouDHh9kBI{jG4;IV zfWZ1LA=J&~M)gVS;_q^0i|xz$6U=J=COjCrDvP%g&%xw2!=X5LB#WzW%*v@vsV7?h zlfUb8{k{n5*1**P%wh2k_)171{rWloGrKj8er8O+#yBh+klry?i?U5|Lk8qEAd|ZR z2O!IwG)HcG`pf#me1&HQ@!Y)NBcBuKB1##MVX3`U?^HmDSFQg7A4KmN`X2sFE6pRU zpz+bp_z=gGE8lq0CWWFnbJ5!~=eE32@|b(m8*<1XXkQt?$)+ANa=m{q&~N9K%0;ix z-l%-yaMfGietr5$MsW2X+g^~9JM zCeV0?LEG2S`nKri()l1CfQaT=|F9o=AZtH|EI zp|4t2^;07MCMS*Df`-a!5%+*np1&;ju!bh(oTjRp(+_TKg2i#`@UB2Nix1_mGvMs{ z*YOQ5eaENin?20nhC?DM7v6e; zPxp}2ONLG~I15sH85@WZHOMq~CJm!HfKep`O}3Iz^=oH6f_;@?Mk#mN{cAh=F3))r zVQ{+I$qR>3AfpJ!W`8e_jpOL(JUH)c$n^N(5)#U@E7NAg9+{zvVHTJX;)XrnJ zlx#zc_^nW6du+npwFUU%@jCX6h>)%G4OekV>+a#w>$l3ab5Ls1#`HSad@QSt5#;AX52siRCngFHkke}>)Fm586@FW)t?p#Uh9V7*DBUyoY z37ZJ;3V{&0MMDEnLPKyoEEw}`=&3&(2D7|t2QK#4%W|DlMs)&z#I;$rzMlP#H*hRX zDjOpnI$a^kwI-J#UV^QnVusc@`W3r{nlP%3(Z{2Tr^-rzb?B}FeuMpSd;)cSwdVIp z-))d2q38^EAd)KoG@0`zImIg0yFTIQ^`E@}l~MqvZwGmX$jPAxUm~$`h`)V(&&~cQ z_X*Ek4gB{JA^&7@tFj)>=ixtYUQCE(r47E2Cw;+g+6E$c5}E7hGuArv=53!trZ@KM z`cYX(ob>k?D-k3c>jbe3ohEHf@4p=+m3kfZIaAiNKg5NX#{`repa&SgzwK$IlHsEE z_r(!YDRkuXQ}I5ynLQachVwDXFiL*TaXDShPFk^qRN4mp>Z2p;m?dLsOA+nMo<%+$_d%rZ}u$M;B}=* zr#j(-`xDeYy~PX8<>EHA+Uah78kY0BVcCh)hFgV^A?o)|kv31}%fakh)wK1r7Dx2P zN%m%pg6w*eM6oh9WqxEcp^B9;8BX|-+cc_pcUOe$j`(z=DQXoPC?pg0ATjeP!HYZ1 zzCBI1InVYT8Qk>@i1BgC*L4?U9T+y0{(ZyL<VMI0klLLRm2M7O&5K zL2f!AR3P(IF>8?+!GRuz)vu+lL{k|QieQIUA{4Uj63qrai%PnLyQuQ^NyCjd>kUGm zYADm|VSHk79-8HO8HzKTnZ5~8#Os^*Egei%_ z|8Age{Z9imvn-U+LvM&_44<@Y)Z@~dj|>?k|F}&orRnT%3#Q3FIxL&w{?XtJt5zTv zm-_Ck#j#)5hDR41+9LZh&uEm6Iy#sDj=3vDbtT}Yw}^^OPdWLAm<{&@>giaOws-Wf z^+?0yS7QKE+aXB7$}=zGhN*4LRm4N2Ey8UzNIcXkyucs9a(Bp{!)*!hta;0EfrOk2 zh~6F-iIyKmyjt?*?mMe?@ob{VX~K=4UNX4AxPraQyd)EOwx?SZJG==Ou6Vt-ApLj@TtGkYQI5D zE*A8pZ)5fi2#;W-eg!}O1>lmODA7fdWuX;4)lTI0jPbfAZPxj$tobrLq5Wf>jNGbD zHPo~hZ12=Z3?ZW$MW=rATNpcg=1X(!LPD5TktaD+Nrb$tYRqj)7%U24Xnk_frRn^7 zlGvh}_R1`6^UER+)5HQo`+XyaxziahGMwy(bWL)d-fLyq$IpTxBxv*m57I~{y6mQ3 zR7C~89xk2f{2|s5r4kRLg^G^+uBmRA1vv6lxb##X9eDUJ<-r^IxBTqTJ@suvxbm?({N{7bzMa>X4>IX&PMoWDIbBge~ z_3hJ4hy6s)4%fxJ(K78|_FT@eSXyt{hy-R*8f4(4%dG*@b8U5fh4i#604`NPj88er zn--*npb{Bd5x1x$d%^G5MIE{q2i&L5Y(rNAVGB00Q)()D#i&9}sue2@NCquNN@VVkn){PIw35c)NNoQW2iv*YpnTNVuB%Hh(85jrc0>l8RXH;;jK6Jt7<;p%!xU} z2HWotUNt~N6Dv7y_a%ZYhfwXSdIsrFz%5-%BW(2XcVA7oByp!E?aAUlsI15zQdf=7 zxTRR+R|6u<{-=w^N*zicd!FCZl<9NKa`JL1x*q9xWxcoIowK7pdkoxWd>XF*P#*bB zt7PQr{~7wu-Uyn5D?$zUcFK@{J5^C^NR;aV2$T1J(6{o98yaA+fp&Bh1$oZ4!-`D- zbpvsVttXMgu1AT6j{{skk4KR<$ye#&> z8?Zz0-1Rm(?Bzsl4-$k09Vqz)#LA6&V1Q_{1cn1z(ez#T^+;Z*a0l8gyQ3zX*t)3x zKRLdJxBfHR(RgT+mEAb)>=HZHl5g1`KNu1)-Dk>F4<6kd;W}HsY2?jgS>so)=8?pG?O*a%=PiOjFqYR3A~1Yz`u651&Qa*=PDT_`uLtaZjIf zEb+?!y3#KHuPg1^t0xLx}-&vv8Hu4jMSQqen-o<3M>>0*<>_q^M_Z@6htE7cq)o=1z9JHSP17vQ}zR(+Yh|{B}q3 z^4h75>)+0Z$PU+8mA$slMt$=r-G+-*%;g%K=ttiK!{-01=`9=rhawXJ)SdM>u{;Vn zJ%IZPgvQBS*_CM==?!J73ptE^#jL3SdFF4U{Hy$}r9}hGm9ggiQcmt7L+aQ4Y+xi| zP*OJRxwgMuaQ_8Skq;ad{jlJJ=>PaNGyuk$yJ8#V-E-Dn7ya*X?tf``7dlD;S>CkQ z|4QWiw|@6LBs;e9URFZW4iK8mzC?7%5mrLegGn!1bs63l+EUkFxeof;+yXLt^Bw8j?greHl3UMYx}vrZIO+%5%i8C?IEnva>rf!#UxTt7|{Kfsfud zzQ^KIfXLLhp;06O$)llr!~hQYy<-n~-buK#kZTScZEAs|&22Vtw7HI>E}>TT*#G;s z&Ih;k+uTD#gW}FU`&gnZ*kk8G+I0DBeS@|wyzAP%fqNT|#W`|OD<>uKnYV`rQSQt9 zQGYZR$dm2{a4}?`i-9Ms_zhAnh{TD|0@w4NqqP}#4&Y}Sq{SE;FJPD z!a6CT?j&t$g((h!9z?Sn)4N^ZFWgT3iW_zZCnaigmsME$msLxSD-fqVZSe=^CaTDr zw5qV337nch=dCiklnjiT=r{=iQ$Rxs%LDHV+?bMd8v_~KMuMWJ`C0m7FPCoXkODwp z>;`k}TURo9?cG$?wpo{qgR9a{`-(~#I=?(=vG|_|`h}-A0pC*htTrS--3vYBSpqqy z6KzDd|CNAGY^lvI4ffUQ`a4HP;^4VJkB|$Y{gN}&r-6r5EE#x6Ij?v~ao1c~VhRE5 zjy#n@1n;u9tKAbxm|IzyIiQXxpl>ea23h(8$Z_G|#PP_o|H=vEe1bhdPH^^Ih{}<# zMziJhg^w;i_aS{DZ+bdFJX_KMdCvA&Hvw208Nnh21*Xe$GYIR1HnPVUSDKCYGjO3WD0KCuVK_));M zhd{>l5cQ$olWq5x@+eM`5begPO%K4uGUOoh~=~1m)9nU~cLbl|*qa#HY$#&_VE|U!QTXsBy zk%qexnH5yAF6;j6d`NK1!(WOA|5>-kN#eSw2;fi8!L_-y1A6|%Mw7ijS5Et4@`Q!j3`s|g}>JsR60O|A3hBA*Vg;} z^jVXrx>9xgkK)(?^kcL%^SehMbLy$^A^_*K^MB3#@0(>7e+#<;F|^cp76TWR|1~lb~&+a{z%Sq9B{8!v(J}pSM zy!GT)+14`;3`->LU^f~a*clbNq_ps_#4G9o8we9kik;Wai9r920e&_3dX^yeaTcmj z#p`!SgK_^~;;;Ptgh_rIDN($)<;rB^uWMnzsRKZi)+hk%2KeWHB@iW;|F6X34&nFZ zbNgQnFbx_+d3H;Y-T4$}QxF0+PAmhdGa=eeWFf%+h4}~S>+vsZJuqS0t^O`;k7pbX}hpy#_j1ujg?7z z4>`w?xyn#i`nlR0&2!%3o1|Xx20)KE253()IMP8_=b$DC!Nnk{CQgeeNDdZr8w_@vDbkfVwn4~WR|Yog{L?! z*Gxnk4jIMW))=Fm?!7(F`DaVyrl)dm#t+UsEYGoXQ& z#}O44A(G2$0^RD|n(>H^N@K04kfdooH38-F2d3l7g<5c1Hs1ye6B9HMCoXMBiqpba z=PtJPCfM_O2+bkVp;B?Ed)uiqpk>|!CePDzFkkQ%#!h!a*^LaqeTlEe4YI+h{%b{t za_B@m7%8y_!JXg?k4?RY;@WdiX&4oPrk5?$p0JC`^H0L;Ciu6pm8~^b!KSsTLP>1w zML0t{?(xBwXFtyEEEev3Jr^)wZhZS(Vfow9rQu+20!83oRuon06X7<~U21ccUgo!S zba-Ly3A?Z^?}|y`bGr8q#h> zt0P2g3wvn0^~sEeFqNo2Ks_KKUSLTLasgywrf>L%rjbyc|*?C zk%+-xJO7o~>tuDs<4=$9e$3$L?-9Z1*ze_1GYRdRx6dy}&fHO<)sm`9RJWekuQ76X z6%sK$IZylkR^TssiZLg(GAoLOip-Y$-J(*{cVZc#H#ow+z+zwoKvbwV*>cu_9O~c# z4pD-2VE#GA|A|A6mMga9DJVQeRyyhVe~vq&{rTBl0T7o@fmz(OoQ854oVi z0tf}GcrD2PmRQ$br47CQ9ngbM(~6-uedpm1oscMyU<>Azt{S> zi9dy@@((><^Ls+yuxIc#k?r!5?5g$L_e5BFjA$xqY}k~`-i-AJVE*ch@lOe4!3{(k zy27uyS@0{kEMQ2b4VysPfMgbqTfW@}#xjD`Lc$DdR0~YIyH^Kf9Ju>V)7JHFw5M1lLBWEXl!0I{K{^|qUu5ACGhz29cVGls1{GX$~Q(H z@&~(`(CY~)xZ6x6Fcgg@0Q|J9bNn0_pn%L&sLp*#5U$zln420(6~u@tnfR_0_GW8o zSf1d;`$Lrlq^v07MbRT+BnPF01A)f6USZh`&~LPFh_@4ckZV{jZa*@Jp6+-d?$+2; z17VV3!#2hi7@J`2jJlmC^ia7`4|33_P-2?bx{?zXsvbHQk2wYPJyLf0{xrY)?@qr! zJ$4q`R(%pXvWHln^I_3oe&J1jpw8@wUa=@s`v;+hKCq zZ>6tyqIVDPRYg^A|K5s;rAwEScn4PN-ITZ0lB` yRfTbY^0~eiqHc8l89t?hEYq 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%0D2=6RYEYN$d_C?`p)c5tp!XLWMEsb!K53 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