diff --git a/README.md b/README.md index c9004c3..af04f61 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ This integration allows to monitor Bluetooth Low Energy (BLE) battery management - JK BMS, Jikong, (HW version >=11 required) - Offgridtec LiFePo4 Smart Pro: type A & B (show up as `SmartBat-A`… or `SmartBat-B`…) - LiTime, Redodo batteries -- Seplos v2 (show up as `BP00`…) +- Seplos v2 (show up as `BP0`?) - Seplos v3 (show up as `SP0`… or `SP1`…) New device types can be easily added via the plugin architecture of this integration. See the [contribution guidelines](CONTRIBUTING.md) for details. diff --git a/custom_components/bms_ble/manifest.json b/custom_components/bms_ble/manifest.json index 2d95f3e..3911fa4 100644 --- a/custom_components/bms_ble/manifest.json +++ b/custom_components/bms_ble/manifest.json @@ -69,7 +69,7 @@ "manufacturer_id": 65535 }, { - "local_name": "BP00", + "local_name": "BP0?", "service_uuid": "0000ff00-0000-1000-8000-00805f9b34fb" } ], diff --git a/custom_components/bms_ble/plugins/basebms.py b/custom_components/bms_ble/plugins/basebms.py index 66cd2a1..fd670a1 100644 --- a/custom_components/bms_ble/plugins/basebms.py +++ b/custom_components/bms_ble/plugins/basebms.py @@ -238,6 +238,7 @@ def crc_modbus(data: bytearray) -> int: crc = (crc >> 1) ^ 0xA001 if crc % 2 else (crc >> 1) return crc & 0xFFFF + def crc_xmodem(data: bytearray) -> int: """Calculate CRC-16-CCITT XMODEM.""" crc: int = 0x0000 @@ -247,6 +248,7 @@ def crc_xmodem(data: bytearray) -> int: crc = (crc << 1) ^ 0x1021 if (crc & 0x8000) else (crc << 1) return crc & 0xFFFF + def crc_sum(frame: bytes) -> int: """Calculate frame CRC.""" return sum(frame) & 0xFF diff --git a/custom_components/bms_ble/plugins/seplos_v2_bms.py b/custom_components/bms_ble/plugins/seplos_v2_bms.py index 024e855..dc83d7a 100644 --- a/custom_components/bms_ble/plugins/seplos_v2_bms.py +++ b/custom_components/bms_ble/plugins/seplos_v2_bms.py @@ -53,7 +53,10 @@ class BMS(BaseBMS): (KEY_PACK_COUNT, 0x51, 42, 1, False, lambda x: min(x, BMS._MAX_SUBS)), (KEY_TEMP_SENS, 0x62, 14, 1, False, lambda x: x), ] # Protocol Seplos V2 (parallel data 0x62, device manufacturer info 0x51) - _CMDS: Final[list[int]] = list({field[1] for field in _FIELDS}) + _CMDS: Final[list[tuple[int, bytes]]] = [ + *list({(field[1], b"") for field in _FIELDS}), + (0x61, b"\x00"), + ] def __init__(self, ble_device: BLEDevice, reconnect: bool = False) -> None: """Initialize BMS.""" @@ -68,7 +71,7 @@ def matcher_dict_list() -> list[dict[str, Any]]: """Provide BluetoothMatcher definition.""" return [ { - "local_name": "BP00", + "local_name": "BP0?", "service_uuid": BMS.uuid_services()[0], "connectable": True, } @@ -97,9 +100,10 @@ def uuid_tx() -> str: @staticmethod def _calc_values() -> set[str]: return { - ATTR_POWER, ATTR_BATTERY_CHARGING, ATTR_CYCLE_CAP, + ATTR_DELTA_VOLTAGE, + ATTR_POWER, ATTR_RUNTIME, } # calculate further values from BMS provided set ones @@ -184,13 +188,11 @@ def _decode_data(data: dict[int, bytearray]) -> dict[str, int | float]: } @staticmethod - def _cell_voltages(data: bytearray, offset: int = 0) -> dict[str, float]: + def _cell_voltages(data: bytearray) -> dict[str, float]: return { - f"{KEY_CELL_VOLTAGE}{idx+offset}": float( + f"{KEY_CELL_VOLTAGE}{idx}": float( int.from_bytes( - data[10 + idx * 2 : 10 + idx * 2 + 2], - byteorder="big", - signed=False, + data[10 + idx * 2 : 10 + idx * 2 + 2], byteorder="big", signed=False ) ) / 1000 @@ -200,29 +202,12 @@ def _cell_voltages(data: bytearray, offset: int = 0) -> dict[str, float]: async def _async_update(self) -> BMSsample: """Update battery status information.""" - for cmd in BMS._CMDS: - await self._client.write_gatt_char(BMS.uuid_tx(), data=BMS._cmd(cmd)) + for cmd, data in BMS._CMDS: + await self._client.write_gatt_char(BMS.uuid_tx(), data=BMS._cmd(cmd, data=bytearray(data))) await asyncio.wait_for(self._wait_event(), timeout=BAT_TIMEOUT) result: BMSsample = BMS._decode_data(self._data_final) - - total_cells: int = 0 - for pack in range(int(result.get(KEY_PACK_COUNT, 0) + 1)): - await self._client.write_gatt_char( - BMS.uuid_tx(), data=BMS._cmd(0x61, address=pack, data=bytearray(1)) - ) - await asyncio.wait_for(self._wait_event(), timeout=BAT_TIMEOUT) - pack_cells: dict[str, float] = BMS._cell_voltages( - self._data_final[0x61], total_cells - ) - result |= pack_cells - result |= { - ATTR_DELTA_VOLTAGE: max( - float(result.get(ATTR_DELTA_VOLTAGE, 0)), - round(max(pack_cells.values()) - min(pack_cells.values()), 3), - ) - } - total_cells += self._data_final[0x61][9] + result |= BMS._cell_voltages(self._data_final[0x61]) self._data_final.clear() diff --git a/tests/test_seplos_v2_bms.py b/tests/test_seplos_v2_bms.py index 6d67734..e8b4836 100644 --- a/tests/test_seplos_v2_bms.py +++ b/tests/test_seplos_v2_bms.py @@ -125,38 +125,6 @@ async def test_update(monkeypatch, reconnect_fixture) -> None: "cell#13": 3.312, "cell#14": 3.313, "cell#15": 3.313, - "cell#16": 3.312, - "cell#17": 3.313, - "cell#18": 3.313, - "cell#19": 3.313, - "cell#20": 3.313, - "cell#21": 3.312, - "cell#22": 3.313, - "cell#23": 3.315, - "cell#24": 3.311, - "cell#25": 3.312, - "cell#26": 3.313, - "cell#27": 3.313, - "cell#28": 3.313, - "cell#29": 3.312, - "cell#30": 3.313, - "cell#31": 3.313, - "cell#32": 3.312, - "cell#33": 3.313, - "cell#34": 3.313, - "cell#35": 3.313, - "cell#36": 3.313, - "cell#37": 3.312, - "cell#38": 3.313, - "cell#39": 3.315, - "cell#40": 3.311, - "cell#41": 3.312, - "cell#42": 3.313, - "cell#43": 3.313, - "cell#44": 3.313, - "cell#45": 3.312, - "cell#46": 3.313, - "cell#47": 3.313, "delta_voltage": 0.004, "pack_count": 2, }