From a81bf055beded89f9fd3c15c2f24d6b9a42a0b90 Mon Sep 17 00:00:00 2001
From: Manuel <mr-manuel@outlook.it>
Date: Sat, 10 Jun 2023 19:14:10 +0200
Subject: [PATCH] changed unique identifier from string to function function
 can be overridden by BMS battery class

---
 CHANGELOG.md                                  |  1 +
 etc/dbus-serialbattery/battery.py             | 24 +++++++++++++------
 .../bms/battery_template.py                   | 14 +++++++----
 etc/dbus-serialbattery/bms/daly.py            | 14 +++++++----
 etc/dbus-serialbattery/bms/heltecmodbus.py    | 11 +++++++--
 etc/dbus-serialbattery/bms/jkbms.py           | 11 +++++++--
 etc/dbus-serialbattery/bms/jkbms_ble.py       | 11 ++++++++-
 etc/dbus-serialbattery/dbushelper.py          |  2 +-
 8 files changed, 65 insertions(+), 23 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94b8e2ab..84bfb4bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## v1.0.x
 * Added: Bluetooth: Show signal strenght of BMS in log by @mr-manuel
+* Added: Create unique identifier, if not provided from BMS by @mr-manuel
 * Added: Exclude a device from beeing used by the dbus-serialbattery driver by @mr-manuel
 * Added: Implement callback function for update by @seidler2547
 * Added: JKBMS BLE - Show last five characters from the MAC address in the custom name (which is displayed in the device list) by @mr-manuel
diff --git a/etc/dbus-serialbattery/battery.py b/etc/dbus-serialbattery/battery.py
index 79ddce46..a8c410a3 100644
--- a/etc/dbus-serialbattery/battery.py
+++ b/etc/dbus-serialbattery/battery.py
@@ -71,15 +71,12 @@ def __init__(self, port, baud, address):
         self.max_battery_discharge_current = None
         self.has_settings = 0
 
-        self.init_values()
-
-        # used to identify a BMS when multiple BMS are connected - planned for future use
-        self.unique_identifier = None
-
         # fetched from the BMS from a field where the user can input a custom string
         # only if available
         self.custom_field = None
 
+        self.init_values()
+
     def init_values(self):
         self.voltage = None
         self.current = None
@@ -131,6 +128,20 @@ def test_connection(self) -> bool:
         # return false when failed, true if successful
         return False
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        If not provided by the BMS/driver then the hardware version and capacity is used,
+        since it can be changed by small amounts to make a battery unique.
+        On +/- 5 Ah you can identify 11 batteries
+        """
+        return (
+            "".join(filter(str.isalnum, self.hardware_version))
+            + "_"
+            + str(self.capacity)
+            + "Ah"
+        )
+
     def connection_name(self) -> str:
         return "Serial " + self.port
 
@@ -1005,8 +1016,7 @@ def log_settings(self) -> None:
         logger.info(
             f"> CCCM SOC: {str(utils.CCCM_SOC_ENABLE).ljust(5)} | DCCM SOC: {utils.DCCM_SOC_ENABLE}"
         )
-        if self.unique_identifier is not None:
-            logger.info(f"Serial Number/Unique Identifier: {self.unique_identifier}")
+        logger.info(f"Serial Number/Unique Identifier: {self.unique_identifier()}")
 
         return
 
diff --git a/etc/dbus-serialbattery/bms/battery_template.py b/etc/dbus-serialbattery/bms/battery_template.py
index e32e424b..92c7fa49 100644
--- a/etc/dbus-serialbattery/bms/battery_template.py
+++ b/etc/dbus-serialbattery/bms/battery_template.py
@@ -36,6 +36,15 @@ def test_connection(self):
 
         return result
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        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 remove this function
+        """
+        return self.serialnumber
+
     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
@@ -53,11 +62,6 @@ def get_settings(self):
         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):
diff --git a/etc/dbus-serialbattery/bms/daly.py b/etc/dbus-serialbattery/bms/daly.py
index 87510c48..732af406 100644
--- a/etc/dbus-serialbattery/bms/daly.py
+++ b/etc/dbus-serialbattery/bms/daly.py
@@ -522,13 +522,17 @@ def read_battery_code(self, ser):
                 " ",
                 (battery_code.strip()),
             )
-            self.unique_identifier = self.custom_field.replace(" ", "_")
-        else:
-            self.unique_identifier = (
-                str(self.production) + "_" + str(int(self.capacity))
-            )
         return True
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        """
+        if self.custom_field != "":
+            return self.custom_field.replace(" ", "_")
+        else:
+            return str(self.production) + "_" + str(int(self.capacity))
+
     def reset_soc_callback(self, path, value):
         if value is None:
             return False
diff --git a/etc/dbus-serialbattery/bms/heltecmodbus.py b/etc/dbus-serialbattery/bms/heltecmodbus.py
index e7f866a0..1ceaa43e 100644
--- a/etc/dbus-serialbattery/bms/heltecmodbus.py
+++ b/etc/dbus-serialbattery/bms/heltecmodbus.py
@@ -30,6 +30,7 @@ class HeltecModbus(Battery):
     def __init__(self, port, baud, address):
         super(HeltecModbus, self).__init__(port, baud, address)
         self.type = "Heltec_Smart"
+        self.unique_identifier_tmp = ""
 
     def test_connection(self):
         # call a function that will connect to the battery, send a command and retrieve the result.
@@ -174,7 +175,7 @@ def read_status_data(self):
                     time.sleep(SLPTIME)
 
                     serial1 = mbdev.read_registers(2, number_of_registers=4)
-                    self.unique_identifier = "-".join(
+                    self.unique_identifier_tmp = "-".join(
                         "{:04x}".format(x) for x in serial1
                     )
                     time.sleep(SLPTIME)
@@ -234,7 +235,7 @@ def read_status_data(self):
             logger.info(self.hardware_version)
             logger.info("Heltec-" + self.hwTypeName)
             logger.info("  Dev name: " + self.devName)
-            logger.info("  Serial: " + self.unique_identifier)
+            logger.info("  Serial: " + self.unique_identifier_tmp)
             logger.info("  Made on: " + self.production_date)
             logger.info("  Cell count: " + str(self.cell_count))
             logger.info("  Cell type: " + self.cellType)
@@ -245,6 +246,12 @@ def read_status_data(self):
 
         return True
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        """
+        return self.unique_identifier_tmp
+
     def read_soc_data(self):
         mbdev = mbdevs[self.address]
 
diff --git a/etc/dbus-serialbattery/bms/jkbms.py b/etc/dbus-serialbattery/bms/jkbms.py
index 5fc87901..8925d824 100644
--- a/etc/dbus-serialbattery/bms/jkbms.py
+++ b/etc/dbus-serialbattery/bms/jkbms.py
@@ -10,6 +10,7 @@ class Jkbms(Battery):
     def __init__(self, port, baud, address):
         super(Jkbms, self).__init__(port, baud, address)
         self.type = self.BATTERYTYPE
+        self.unique_identifier_tmp = ""
 
     BATTERYTYPE = "Jkbms"
     LENGTH_CHECK = 1
@@ -184,9 +185,9 @@ def read_status_data(self):
         )[0].decode()
 
         offset = cellbyte_count + 197
-        self.unique_identifier = sub(
+        self.unique_identifier_tmp = sub(
             " +",
-            " ",
+            "_",
             (
                 unpack_from(">24s", self.get_data(status_data, b"\xBA", offset, 24))[0]
                 .decode()
@@ -209,6 +210,12 @@ def read_status_data(self):
         # logger.info(self.hardware_version)
         return True
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        """
+        return self.unique_identifier_tmp
+
     def to_fet_bits(self, byte_data):
         tmp = bin(byte_data)[2:].rjust(3, utils.zero_char)
         self.charge_fet = is_bit_set(tmp[2])
diff --git a/etc/dbus-serialbattery/bms/jkbms_ble.py b/etc/dbus-serialbattery/bms/jkbms_ble.py
index d687e42a..6a2e3011 100644
--- a/etc/dbus-serialbattery/bms/jkbms_ble.py
+++ b/etc/dbus-serialbattery/bms/jkbms_ble.py
@@ -19,6 +19,7 @@ def __init__(self, port, baud, address):
         self.address = address
         self.type = self.BATTERYTYPE
         self.jk = Jkbms_Brn(address)
+        self.unique_identifier_tmp = ""
 
         logger.info("Init of Jkbms_Ble at " + address)
 
@@ -91,7 +92,9 @@ def get_settings(self):
         tmp = self.jk.get_status()["device_info"]["manufacturing_date"]
         self.production = "20" + tmp if tmp and tmp != "" else None
 
-        self.unique_identifier = self.jk.get_status()["device_info"]["serial_number"]
+        self.unique_identifier_tmp = self.jk.get_status()["device_info"][
+            "serial_number"
+        ]
 
         for c in range(self.cell_count):
             self.cells.append(Cell(False))
@@ -109,6 +112,12 @@ def get_settings(self):
         logger.info("BAT: " + self.hardware_version)
         return True
 
+    def unique_identifier(self) -> str:
+        """
+        Used to identify a BMS when multiple BMS are connected
+        """
+        return self.unique_identifier_tmp
+
     def use_callback(self, callback: Callable) -> bool:
         self.jk.set_callback(callback)
         return callback is not None
diff --git a/etc/dbus-serialbattery/dbushelper.py b/etc/dbus-serialbattery/dbushelper.py
index 9edc57b1..946eb4d6 100644
--- a/etc/dbus-serialbattery/dbushelper.py
+++ b/etc/dbus-serialbattery/dbushelper.py
@@ -129,7 +129,7 @@ def setup_vedbus(self):
             onchangecallback=self.battery.custom_name_callback,
         )
         self._dbusservice.add_path(
-            "/Serial", self.battery.unique_identifier, writeable=True
+            "/Serial", self.battery.unique_identifier(), writeable=True
         )
         self._dbusservice.add_path(
             "/DeviceName", self.battery.custom_field, writeable=True