From f85661345c50f5de839058ea8c4253be52ed2302 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 13:58:22 +0300 Subject: [PATCH 01/11] fix(AC500): Change max pack number to 6 --- custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py index 3159dde..75ecd66 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -49,7 +49,7 @@ class AC500(ProtocolV1Device): @property def pack_num_max(self): - return 4 + return 6 @property def polling_commands(self) -> List[ReadHoldingRegisters]: From a54b36a90b9dd140cf2c4a6c27fd8327b84b9948 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:42:04 +0300 Subject: [PATCH 02/11] fix: Initialize `sensor. _unavailable_counter` with a 5 --- custom_components/bluetti_bt/binary_sensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/bluetti_bt/binary_sensor.py b/custom_components/bluetti_bt/binary_sensor.py index 5bb013e..862bd0f 100644 --- a/custom_components/bluetti_bt/binary_sensor.py +++ b/custom_components/bluetti_bt/binary_sensor.py @@ -86,6 +86,7 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): e_name = f"{device_info.get('name')} {name}" self._address = address self._response_key = response_key + self._unavailable_counter = 5 self._attr_device_info = device_info self._attr_name = name From 23551ae82956c238ca2d8587d401a5d9b2afc40d Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:42:32 +0300 Subject: [PATCH 03/11] improvement: Add ARM and DSP versions --- .DS_Store | Bin 0 -> 6148 bytes .../base_devices/ProtocolV1Device.py | 2 ++ .../bluetti_bt_lib/field_attributes.py | 8 ++++++++ 3 files changed, 10 insertions(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..adde28060660a2acf75b8048b5fc8ccfb587e153 GIT binary patch literal 6148 zcmeHKU279T6upzCX17I%pv4zq!PgRF{ea?2nkEkg^`R^Jpc1p)rCGb#4cSdY34uH+ z{s;epZ~heji%)v)%(U!oA}EMRm&vuf71_0}CEQ#b2pleFC*zvkMDB#FGsa^HIy z2Gd^s=3x?N{V)#3(joK%P~N->V?UX;lS%BS($`fTmTlR+`p#_D+;#T$AI*22+5R5N z4<9_9&u#10-Fr`tduP!wPCj5wFo6?T8t-V{KBDp)N}apoI7;GEuz4h#^@o-b)V#p1 zq%bp1TsEW{C>gFlxJ^Vu~$V+o(adbFdZ@0_FAmQEW#*Y6!@D8@cG~% zF}5@=6w0FmnS25Ot7w*nvi$20bhrW7(zsBF7MM`3K;ii7 zXT~^sW}#mwLO(o&Bk3es3QcYlFbWhE*ih9v-~ZiTzyFI)re+i{3apg^qS|pgZ7fOO ztxJpJyVgN^hs4Ibg+l3qOkc;cz*q5YBx&e#xBzTvTqr~f%={6MGMLOL@J|)^2`4h< AVgLXD literal 0 HcmV?d00001 diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/ProtocolV1Device.py b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/ProtocolV1Device.py index 8160342..a8c4532 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/ProtocolV1Device.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/ProtocolV1Device.py @@ -14,6 +14,8 @@ class ProtocolV1Device(BluettiDevice): # Device info self.struct.add_string_field("device_type", 10, 6) self.struct.add_sn_field("serial_number", 17) + self.struct.add_version_field('arm_version', 23) + self.struct.add_version_field('dsp_version', 25) # Power IO self.struct.add_uint_field("dc_input_power", 36) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/field_attributes.py b/custom_components/bluetti_bt/bluetti_bt_lib/field_attributes.py index db9707e..bcb4182 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/field_attributes.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/field_attributes.py @@ -125,6 +125,14 @@ FIELD_ATTRIBUTES: dict[str, FieldAttributes] = { "ac_input_power": PowerFieldAttributes("AC Input Power"), "ac_output_power": PowerFieldAttributes("AC Output Power"), "dc_output_power": PowerFieldAttributes("DC Output Power"), + "dsp_version": FieldAttributes( + type=FieldType.NUMERIC, + name=f"DSP Version", + ), + "arm_version": FieldAttributes( + type=FieldType.NUMERIC, + name=f"ARM Version", + ), "power_generation": EnergyFieldAttributes("Total Power Generation"), "total_battery_percent": FieldAttributes( type=FieldType.NUMERIC, From 0f33a31ee559ba5fbb58e7e4b4dfe26c37f6cb71 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:44:44 +0300 Subject: [PATCH 04/11] fix: Correctly read battery packs' values - wait before setting the target pack number #109 --- .../bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py index 6dd6fd0..bbb3be4 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py @@ -117,6 +117,11 @@ class DeviceReader: _LOGGER.warning("Pack polling failed (pack_num %i doesn't match expected %i)", set_pack, pack) continue + if self.bluetti_device.pack_num_max > 1: + # We need to wait after switching packs + # for the data to be available + await asyncio.sleep(5) + for command in pack_commands: # Request & parse result for each pack try: From 0e5c1a0b440bdd61dc79c9730b63f75bc47768c1 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:45:24 +0300 Subject: [PATCH 05/11] fix: Increase polling interval and timeout #109 --- custom_components/bluetti_bt/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/bluetti_bt/__init__.py b/custom_components/bluetti_bt/__init__.py index 8d68e05..16fb215 100644 --- a/custom_components/bluetti_bt/__init__.py +++ b/custom_components/bluetti_bt/__init__.py @@ -38,9 +38,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: address = entry.data.get(CONF_ADDRESS) device_name = entry.data.get(CONF_NAME) use_controls = entry.data.get(CONF_USE_CONTROLS) - polling_interval = entry.data.get(CONF_POLLING_INTERVAL, 20) + polling_interval = entry.data.get(CONF_POLLING_INTERVAL, 60) persistent_conn = entry.data.get(CONF_PERSISTENT_CONN, False) - polling_timeout = entry.data.get(CONF_POLLING_TIMEOUT, 45) + polling_timeout = entry.data.get(CONF_POLLING_TIMEOUT, 120) max_retries = entry.data.get(CONF_MAX_RETRIES, 5) if address is None: From a1e9c180b32940ea565687307b5e78a190b5391b Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:45:59 +0300 Subject: [PATCH 06/11] improvement: Automatically build polling commands basing on the fields --- .../bluetti_bt_lib/bluetooth/device_reader.py | 4 ++- .../bluetti_bt_lib/devices/ac200l.py | 16 ++++------ .../bluetti_bt_lib/devices/ac200m.py | 16 ++++------ .../bluetti_bt_lib/devices/ac300.py | 19 +++--------- .../bluetti_bt_lib/devices/ac500.py | 21 ++++--------- .../bluetti_bt/bluetti_bt_lib/devices/eb3a.py | 8 +---- .../bluetti_bt_lib/devices/ep500.py | 21 ++++--------- .../bluetti_bt_lib/devices/ep500p.py | 21 ++++--------- .../bluetti_bt_lib/devices/ep600.py | 26 +--------------- .../bluetti_bt/bluetti_bt_lib/utils/struct.py | 30 +++++++++++++++++++ 10 files changed, 69 insertions(+), 113 deletions(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py index bbb3be4..92a2eac 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py @@ -53,6 +53,8 @@ class DeviceReader: if filter_registers is not None: polling_commands = filter_registers pack_commands = [] + _LOGGER.info("Pooling comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in polling_commands])) + _LOGGER.info("Pack comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in pack_commands])) parsed_data: dict = {} @@ -142,7 +144,7 @@ class DeviceReader: _LOGGER.warning("Got a parse exception...") except TimeoutError: - _LOGGER.warning("Polling timed out. Trying again later") + _LOGGER.warning(f"Polling timed out ({self.polling_timeout}s). Trying again later") return None except BleakError as err: _LOGGER.error("Bleak error: %s", err) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py index e1d5fa6..1c74219 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py @@ -28,6 +28,7 @@ class AC200L(ProtocolV1Device): self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal + self.struct.add_version_field("pack_bms_version", 201) # Controls self.struct.add_bool_field("power_off", 3060) @@ -39,12 +40,9 @@ class AC200L(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 5), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3060, 2), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -53,8 +51,6 @@ class AC200L(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16) + ReadHoldingRegisters(91, 37), + ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py index fb0d97c..addc485 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py @@ -28,6 +28,7 @@ class AC200M(ProtocolV1Device): self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal + self.struct.add_version_field("pack_bms_version", 201) # Controls self.struct.add_bool_field("power_off", 3060) @@ -39,12 +40,9 @@ class AC200M(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 5), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3060, 2), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -53,8 +51,6 @@ class AC200M(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16) + ReadHoldingRegisters(91, 37), + ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py index e7a9303..7311ac6 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py @@ -54,17 +54,9 @@ class AC300(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 11), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3001, 1), - ReadHoldingRegisters(3004, 2), - ReadHoldingRegisters(3011, 1), - ReadHoldingRegisters(3013, 1), - ReadHoldingRegisters(3015, 2), - ReadHoldingRegisters(3061, 1), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -76,9 +68,6 @@ class AC300(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16), + ReadHoldingRegisters(91, 37), ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py index 75ecd66..b921e44 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -34,6 +34,7 @@ class AC500(ProtocolV1Device): self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal + self.struct.add_version_field('pack_bms_version', 201) # Controls self.struct.add_enum_field("ups_mode", 3001, UpsMode) @@ -53,17 +54,9 @@ class AC500(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 11), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3001, 1), - ReadHoldingRegisters(3004, 2), - ReadHoldingRegisters(3011, 1), - ReadHoldingRegisters(3013, 1), - ReadHoldingRegisters(3015, 2), - ReadHoldingRegisters(3061, 1), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -75,8 +68,6 @@ class AC500(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16), + ReadHoldingRegisters(91, 37), + ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/eb3a.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/eb3a.py index 644ac26..7b9eb56 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/eb3a.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/eb3a.py @@ -25,13 +25,7 @@ class EB3A(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(77, 1), - ReadHoldingRegisters(86, 1), - ReadHoldingRegisters(3034, 1), - ReadHoldingRegisters(3060, 1), - ReadHoldingRegisters(3063, 5), - ] + return self.struct.get_read_holding_registers() @property def writable_ranges(self) -> List[range]: diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py index 1554ce9..78a7a6c 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py @@ -34,6 +34,7 @@ class EP500(ProtocolV1Device): self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal + self.struct.add_version_field("pack_bms_version", 201) # Controls self.struct.add_enum_field("ups_mode", 3001, UpsMode) @@ -49,17 +50,9 @@ class EP500(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 11), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3001, 1), - ReadHoldingRegisters(3004, 2), - ReadHoldingRegisters(3011, 1), - ReadHoldingRegisters(3013, 1), - ReadHoldingRegisters(3015, 2), - ReadHoldingRegisters(3061, 1), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -71,8 +64,6 @@ class EP500(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16), + ReadHoldingRegisters(91, 37), + ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py index ba19241..740e580 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py @@ -34,6 +34,7 @@ class EP500P(ProtocolV1Device): self.struct.add_decimal_field("pack_voltage", 98, 2) # Full pack voltage self.struct.add_uint_field("pack_battery_percent", 99) self.struct.add_decimal_array_field("cell_voltages", 105, 16, 2) # internal + self.struct.add_version_field("pack_bms_version", 201) # Controls self.struct.add_enum_field("ups_mode", 3001, UpsMode) @@ -49,17 +50,9 @@ class EP500P(ProtocolV1Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(70, 11), - ReadHoldingRegisters(86, 3), - ReadHoldingRegisters(92, 1), - ReadHoldingRegisters(3001, 1), - ReadHoldingRegisters(3004, 2), - ReadHoldingRegisters(3011, 1), - ReadHoldingRegisters(3013, 1), - ReadHoldingRegisters(3015, 2), - ReadHoldingRegisters(3061, 1), - ] + return self.struct.get_read_holding_registers( + filter=lambda address: address <= 90 or address >= 256 # pack_polling_commands + ) @property def writable_ranges(self) -> List[range]: @@ -71,8 +64,6 @@ class EP500P(ProtocolV1Device): @property def pack_polling_commands(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(91, 2), - ReadHoldingRegisters(96, 1), - ReadHoldingRegisters(98, 2), - ReadHoldingRegisters(105, 16), + ReadHoldingRegisters(91, 37), + ReadHoldingRegisters(201, 2), ] diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep600.py b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep600.py index 282d1f4..8404a0e 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep600.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep600.py @@ -97,28 +97,4 @@ class EP600(ProtocolV2Device): @property def polling_commands(self) -> List[ReadHoldingRegisters]: - return super().polling_commands + [ - ReadHoldingRegisters(2022, 2), - ReadHoldingRegisters(2213, 5), - ReadHoldingRegisters(1212, 3), - ReadHoldingRegisters(1220, 3), - ReadHoldingRegisters(1228, 3), - ReadHoldingRegisters(1236, 3), - ReadHoldingRegisters(1244, 3), - ReadHoldingRegisters(1300, 1), - ReadHoldingRegisters(1313, 3), - ReadHoldingRegisters(1319, 3), - ReadHoldingRegisters(1325, 3), - ReadHoldingRegisters(1500, 1), - ReadHoldingRegisters(1510, 3), - ReadHoldingRegisters(1517, 3), - ReadHoldingRegisters(1524, 3), - ReadHoldingRegisters(152, 1), - ReadHoldingRegisters(156, 1), - ReadHoldingRegisters(158, 1), - # KM - Consumption - ReadHoldingRegisters(1430, 15), - # KM - Totals - ReadHoldingRegisters(142, 5), - - ] + return self.struct.get_read_holding_registers() diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py b/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py index 70ef49e..a4987a1 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py @@ -4,6 +4,7 @@ from decimal import Decimal from enum import Enum +from .commands import ReadHoldingRegisters import struct from typing import Any, List, Optional, Tuple, Type @@ -179,6 +180,35 @@ class DeviceStruct: def add_sn_field(self, name: str, address: int): self.fields.append(SerialNumberField(name, address)) + + def get_read_holding_registers(self, tolerance=20, filter=lambda address: True): + address_set = set() + for field in self.fields: + for address in range(field.address, field.address + field.size): + address_set.add(address) + # Filter out addresses that we specificly + # specified to exclude. + for address in set(address_set): + if not filter(address): + address_set.remove(address) + # Fast path: no addresses -> no commands. + if not address_set: + return [] + + commands = [] + + address_list = sorted(address_set) + start = address_list[0] + last = address_list[0] + for address in address_list[1:]: + # Create a new group if there's a gap + # in the addresses. + if address >= last + tolerance: + commands.append(ReadHoldingRegisters(start, last - start + 1)) + start = address + last = address + commands.append(ReadHoldingRegisters(start, last - start + 1)) + return commands def parse(self, starting_address: int, data: bytes) -> dict: # Offsets and size are counted in 2 byte chunks, so for the range we From 211f1971c5ea16f4710ae5c1437d28914965e871 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 16:48:09 +0300 Subject: [PATCH 07/11] chore: Remove .DS_Store from the repo --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + 2 files changed, 1 insertion(+) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index adde28060660a2acf75b8048b5fc8ccfb587e153..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKU279T6upzCX17I%pv4zq!PgRF{ea?2nkEkg^`R^Jpc1p)rCGb#4cSdY34uH+ z{s;epZ~heji%)v)%(U!oA}EMRm&vuf71_0}CEQ#b2pleFC*zvkMDB#FGsa^HIy z2Gd^s=3x?N{V)#3(joK%P~N->V?UX;lS%BS($`fTmTlR+`p#_D+;#T$AI*22+5R5N z4<9_9&u#10-Fr`tduP!wPCj5wFo6?T8t-V{KBDp)N}apoI7;GEuz4h#^@o-b)V#p1 zq%bp1TsEW{C>gFlxJ^Vu~$V+o(adbFdZ@0_FAmQEW#*Y6!@D8@cG~% zF}5@=6w0FmnS25Ot7w*nvi$20bhrW7(zsBF7MM`3K;ii7 zXT~^sW}#mwLO(o&Bk3es3QcYlFbWhE*ih9v-~ZiTzyFI)re+i{3apg^qS|pgZ7fOO ztxJpJyVgN^hs4Ibg+l3qOkc;cz*q5YBx&e#xBzTvTqr~f%={6MGMLOL@J|)^2`4h< AVgLXD diff --git a/.gitignore b/.gitignore index 41f0f9a..665af6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ */__pycache__/ *.pyc +.DS_Store From fa9bcf63ca7108a6a8f37a29831c9802d363ff9a Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 29 Jun 2024 17:04:43 +0300 Subject: [PATCH 08/11] fix: Actually retry connecting to a device --- .../bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py index 92a2eac..64575d6 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py @@ -68,8 +68,8 @@ class DeviceReader: await self.client.connect() break except Exception as e: - if attempt == self.max_retries or attempt == 1: - raise # pass exception on max_retries attempt + if attempt == self.max_retries: + raise e # pass exception on max_retries attempt else: _LOGGER.warning( f"Connect unsucessful (attempt {attempt}): {e}. Retrying..." @@ -143,8 +143,8 @@ class DeviceReader: except ParseError: _LOGGER.warning("Got a parse exception...") - except TimeoutError: - _LOGGER.warning(f"Polling timed out ({self.polling_timeout}s). Trying again later") + except TimeoutError as err: + _LOGGER.error(f"Polling timed out ({self.polling_timeout}s). Trying again later", exc_info=err) return None except BleakError as err: _LOGGER.error("Bleak error: %s", err) From da2017a24b7ce894cf1d9487fa684870eedf1b2a Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sun, 30 Jun 2024 17:14:30 +0300 Subject: [PATCH 09/11] chore: Mention that AC500 is tested --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b716e93..ecfc19a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ After the installation, you can use this button to install the integration: - AC200L (untested) - AC200M - AC300 (tested) -- AC500 +- AC500 (tested) - EB3A - EP500 - EP500P From 8967f3379cbde6f7a8a9e3d0db31cc2dcbbfd5c6 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 3 Aug 2024 21:32:28 +0300 Subject: [PATCH 10/11] Fix a typo in the Polling commands text Co-authored-by: Patrick762 --- .../bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py index 64575d6..92b6162 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py @@ -53,7 +53,7 @@ class DeviceReader: if filter_registers is not None: polling_commands = filter_registers pack_commands = [] - _LOGGER.info("Pooling comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in polling_commands])) + _LOGGER.info("Polling commands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in polling_commands])) _LOGGER.info("Pack comands: " + ",".join([f"{c.starting_address}-{c.starting_address + c.quantity - 1}" for c in pack_commands])) parsed_data: dict = {} From 9be8aa47ac53cb0332af19a68daa7cc885f6f048 Mon Sep 17 00:00:00 2001 From: Artem Chepurnyi Date: Sat, 3 Aug 2024 21:36:27 +0300 Subject: [PATCH 11/11] Add dsp/arm_version to diagnostic fields --- custom_components/bluetti_bt/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom_components/bluetti_bt/const.py b/custom_components/bluetti_bt/const.py index 1568e73..9acc314 100644 --- a/custom_components/bluetti_bt/const.py +++ b/custom_components/bluetti_bt/const.py @@ -39,6 +39,8 @@ DIAGNOSTIC_FIELDS = [ "max_ac_output_power", "max_ac_output_current", "bcu_version", + "dsp_version", + "arm_version", "safety_module_version", "high_voltage_module_version", "power_generation",