From 221da9a38d70d0730d84c157bb632b4221aa2dd3 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 11:24:20 +0200 Subject: [PATCH 01/10] fix internal_power sensors for ac300 and ac500 --- .../bluetti_bt/bluetti_bt_lib/devices/ac300.py | 6 +++--- .../bluetti_bt/bluetti_bt_lib/devices/ac500.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 3f6b9e0..11845c1 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py @@ -15,13 +15,13 @@ class AC300(ProtocolV1Device): self.struct.add_enum_field("ac_output_mode", 70, OutputMode) self.struct.add_decimal_field("internal_ac_voltage", 71, 1) self.struct.add_decimal_field("internal_current_one", 72, 1) - self.struct.add_uint_field("internal_power_one", 73) + self.struct.add_int_field("internal_power_one", 73) self.struct.add_decimal_field("internal_ac_frequency", 74, 2) self.struct.add_decimal_field("internal_current_two", 75, 1) - self.struct.add_uint_field("internal_power_two", 76) + self.struct.add_int_field("internal_power_two", 76) self.struct.add_decimal_field("ac_input_voltage", 77, 1) self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) - self.struct.add_uint_field("internal_power_three", 79) + self.struct.add_int_field("internal_power_three", 79) self.struct.add_decimal_field("ac_input_frequency", 80, 2) self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) self.struct.add_decimal_field("internal_dc_input_power", 87, 1) 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 7d53243..b4d2b60 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -15,13 +15,13 @@ class AC500(ProtocolV1Device): self.struct.add_enum_field("ac_output_mode", 70, OutputMode) self.struct.add_decimal_field("internal_ac_voltage", 71, 1) self.struct.add_decimal_field("internal_current_one", 72, 1) - self.struct.add_uint_field("internal_power_one", 73) + self.struct.add_int_field("internal_power_one", 73) self.struct.add_decimal_field("internal_ac_frequency", 74, 2) self.struct.add_decimal_field("internal_current_two", 75, 1) - self.struct.add_uint_field("internal_power_two", 76) + self.struct.add_int_field("internal_power_two", 76) self.struct.add_decimal_field("ac_input_voltage", 77, 1) self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) - self.struct.add_uint_field("internal_power_three", 79) + self.struct.add_int_field("internal_power_three", 79) self.struct.add_decimal_field("ac_input_frequency", 80, 2) self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) self.struct.add_decimal_field("internal_dc_input_power", 87, 1) From 9af3d67ec6655c3a4cb0d0caf4f804a1a75a0019 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 11:34:25 +0200 Subject: [PATCH 02/10] removed binary sensors for disabled control entities to prevent duplicates --- custom_components/bluetti_bt/binary_sensor.py | 10 +--------- .../bluetti_bt/bluetti_bt_lib/utils/struct.py | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/custom_components/bluetti_bt/binary_sensor.py b/custom_components/bluetti_bt/binary_sensor.py index ca6457a..0e09ee7 100644 --- a/custom_components/bluetti_bt/binary_sensor.py +++ b/custom_components/bluetti_bt/binary_sensor.py @@ -36,7 +36,7 @@ async def async_setup_entry( device_name = entry.data.get(CONF_NAME) address = entry.data.get(CONF_ADDRESS) - use_controls = entry.data.get(CONF_USE_CONTROLS) + use_controls = entry.data.get(CONF_USE_CONTROLS, False) if address is None: _LOGGER.error("Device has no address") @@ -52,12 +52,7 @@ async def async_setup_entry( for field_key, field_config in all_fields.items(): if bluetti_device.has_field(field_key): if field_config.type == FieldType.BOOL: - category = None if field_config.setter is True: - category = EntityCategory.DIAGNOSTIC - - # Ignore setters if controls enabled - if field_config.setter is True and use_controls is True: continue sensors_to_add.append( @@ -67,7 +62,6 @@ async def async_setup_entry( address, field_key, field_config.name, - category=category, ) ) @@ -84,7 +78,6 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): address, response_key: str, name: str, - category: EntityCategory | None = None, ): """Init battery entity.""" super().__init__(coordinator) @@ -98,7 +91,6 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): self._attr_name = name self._attr_available = False self._attr_unique_id = get_unique_id(e_name) - self._attr_entity_category = category @property def available(self) -> bool: 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 10fd169..58a9d0c 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py @@ -30,7 +30,7 @@ class DeviceField: class UintField(DeviceField): - def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], scale: int): + def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], scale: float): self.range = range self.scale = scale super().__init__(name, address, 1) @@ -147,7 +147,7 @@ class DeviceStruct: def __init__(self): self.fields = [] - def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, scale: int = 1): + def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, scale: float = 1): self.fields.append(UintField(name, address, range, scale)) def add_int_field(self, name: str, address: int, range: Tuple[int, int] = None): From 2922c5cb906324e626d4683d7bb3cc9d96ea8de7 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 12:05:09 +0200 Subject: [PATCH 03/10] add multiplier to decimal field --- .../bluetti_bt/bluetti_bt_lib/utils/struct.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 58a9d0c..70ef49e 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/utils/struct.py @@ -30,15 +30,15 @@ class DeviceField: class UintField(DeviceField): - def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], scale: float): + def __init__(self, name: str, address: int, range: Optional[Tuple[int, int]], multiplier: float): self.range = range - self.scale = scale + self.multiplier = multiplier super().__init__(name, address, 1) def parse(self, data: bytes) -> int: val = struct.unpack("!H", data)[0] - return val * self.scale + return val * self.multiplier def in_range(self, val: int) -> bool: if self.range is None: @@ -82,15 +82,16 @@ class EnumField(DeviceField): class DecimalField(DeviceField): def __init__( - self, name: str, address: int, scale: int, range: Optional[Tuple[int, int]] + self, name: str, address: int, scale: int, range: Optional[Tuple[int, int]], multiplier: float ): self.scale = scale self.range = range + self.multiplier = multiplier super().__init__(name, address, 1) def parse(self, data: bytes) -> Decimal: val = Decimal(struct.unpack("!H", data)[0]) - return val / 10 ** self.scale + return (val / 10 ** self.scale) * self.multiplier def in_range(self, val: Decimal) -> bool: if self.range is None: @@ -147,8 +148,8 @@ class DeviceStruct: def __init__(self): self.fields = [] - def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, scale: float = 1): - self.fields.append(UintField(name, address, range, scale)) + def add_uint_field(self, name: str, address: int, range: Tuple[int, int] = None, multiplier: float = 1): + self.fields.append(UintField(name, address, range, multiplier)) def add_int_field(self, name: str, address: int, range: Tuple[int, int] = None): self.fields.append(IntField(name, address, range)) @@ -160,9 +161,9 @@ class DeviceStruct: self.fields.append(EnumField(name, address, enum)) def add_decimal_field( - self, name: str, address: int, scale: int, range: Tuple[int, int] = None + self, name: str, address: int, scale: int, range: Tuple[int, int] = None, multiplier: float = 1 ): - self.fields.append(DecimalField(name, address, scale, range)) + self.fields.append(DecimalField(name, address, scale, range, multiplier)) def add_decimal_array_field(self, name: str, address: int, size: int, scale: int): self.fields.append(DecimalArrayField(name, address, size, scale)) From b34d0a3039e8ea7fee68810938c6a1464939151e Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 12:06:21 +0200 Subject: [PATCH 04/10] fix ac300 and ac500 solar string values and renamed entity --- .../bluetti_bt/bluetti_bt_lib/devices/ac300.py | 6 +++--- .../bluetti_bt/bluetti_bt_lib/devices/ac500.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 11845c1..0b7df14 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py @@ -23,9 +23,9 @@ class AC300(ProtocolV1Device): self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) self.struct.add_int_field("internal_power_three", 79) self.struct.add_decimal_field("ac_input_frequency", 80, 2) - self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) - self.struct.add_decimal_field("internal_dc_input_power", 87, 1) - self.struct.add_decimal_field("internal_dc_input_current", 88, 2) + self.struct.add_uint_field("pv_input_voltage1", 86, multiplier=.1) + self.struct.add_decimal_field("pv_input_power1", 87, 1, multiplier=10) + self.struct.add_decimal_field("pv_input_current1", 88, 2, multiplier=10) # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal 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 b4d2b60..8fc25d2 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -23,9 +23,9 @@ class AC500(ProtocolV1Device): self.struct.add_decimal_field("internal_current_three", 78, 1, (0, 100)) self.struct.add_int_field("internal_power_three", 79) self.struct.add_decimal_field("ac_input_frequency", 80, 2) - self.struct.add_uint_field("internal_dc_input_voltage", 86, scale=.1) - self.struct.add_decimal_field("internal_dc_input_power", 87, 1) - self.struct.add_decimal_field("internal_dc_input_current", 88, 2) + self.struct.add_uint_field("pv_input_voltage1", 86, multiplier=.1) + self.struct.add_decimal_field("pv_input_power1", 87, 1, multiplier=10) + self.struct.add_decimal_field("pv_input_current1", 88, 2, multiplier=10) # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal From 6e433e3551f3e00edd90cb71ebe74edf0c253fb6 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 12:14:51 +0200 Subject: [PATCH 05/10] fix total_battery_voltage --- custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py | 1 + custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py | 1 + custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py | 1 + custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py | 1 + custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py | 1 + custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py | 1 + 6 files changed, 6 insertions(+) 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 ab80a10..67be43d 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py @@ -42,6 +42,7 @@ class AC200L(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 5), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3060, 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 79feaea..aa574b1 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py @@ -42,6 +42,7 @@ class AC200M(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 5), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3060, 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 0b7df14..b8fcb64 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py @@ -57,6 +57,7 @@ class AC300(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 11), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3011, 1), 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 8fc25d2..f4ac5ff 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -56,6 +56,7 @@ class AC500(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 11), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3011, 1), 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 59101d6..3bf7192 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py @@ -52,6 +52,7 @@ class EP500(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 11), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3011, 1), 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 62b62de..e3748ae 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py @@ -52,6 +52,7 @@ class EP500P(ProtocolV1Device): return super().polling_commands + [ ReadHoldingRegisters(70, 11), ReadHoldingRegisters(86, 3), + ReadHoldingRegisters(92, 1), ReadHoldingRegisters(3001, 1), ReadHoldingRegisters(3004, 2), ReadHoldingRegisters(3011, 1), From b548528085f3b44e63ae4e5057531c64daafee0e Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 12:55:46 +0200 Subject: [PATCH 06/10] updated pack polling --- .../base_devices/BluettiDevice.py | 5 ++ .../base_devices/ProtocolV1Device.py | 6 ++ .../bluetti_bt_lib/bluetooth/device_reader.py | 55 +++++++++++-------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py index dcc5cd7..44a16c3 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py @@ -42,6 +42,11 @@ class BluettiDevice: def writable_ranges(self) -> List[range]: """The address ranges that are writable""" return [] + + @property + def pack_num_field(self) -> List[ReadHoldingRegisters]: + """The address 'range' of the pack num""" + return [] def has_field(self, field: str): return any(f.name == field for f in self.struct.fields) 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 7f1f585..91bd5a4 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 @@ -56,3 +56,9 @@ class ProtocolV1Device(BluettiDevice): @property def writable_ranges(self) -> List[range]: return [range(3006, 3009)] + + @property + def pack_num_field(self) -> List[ReadHoldingRegisters]: + return [ + ReadHoldingRegisters(3006, 1), + ] 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 0e04564..a88beb0 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 @@ -97,20 +97,45 @@ class DeviceReader: _LOGGER.warning("Got a parse exception") # Execute pack polling commands - if len(pack_commands) > 0: + if len(pack_commands) > 0 and len(self.bluetti_device.pack_num_field) == 1: _LOGGER.debug("Polling battery packs") for pack in range(1, self.bluetti_device.pack_num_max + 1): + _LOGGER.debug("Setting pack_num to %i", pack) + # Set current pack number - await self._async_send_command( - self.bluetti_device.build_setter_command( - "pack_num", pack - ) + command = self.bluetti_device.build_setter_command( + "pack_num", pack ) + await self._async_send_command(command) - # We need to wait after switching packs for the data to be available - await asyncio.sleep(5) + try: + # Read pack_num field + command = self.bluetti_device.pack_num_field[0] + body = command.parse_response( + await self._async_send_command(command) + ) + _LOGGER.debug("Raw data: %s", body) + parsed = self.bluetti_device.parse( + command.starting_address, body + ) + _LOGGER.debug("Parsed data: %s", parsed) - pack_ok = False + # Check pack number + pack_number = parsed.get("pack_num") + if ( + not isinstance(pack_number, int) + or pack_number != pack + ): + _LOGGER.debug( + "Parsed pack_num(%s) does not match expected '%s', Skipping pack (maybe you don't have this connected)", + pack_number, + pack, + ) + continue + + except ParseError: + _LOGGER.warning("Got a pack parse exception, Skipping pack") + continue for command in pack_commands: # Request & parse result for each pack @@ -123,20 +148,6 @@ class DeviceReader: ) _LOGGER.debug("Parsed data: %s", parsed) - if not pack_ok: # Only check this once - pack_number = parsed.get("pack_num") - if ( - not isinstance(pack_number, int) - or pack_number != pack - ): - _LOGGER.debug( - "Parsed pack_num(%s) does not match expected '%s'", - pack_number, - pack, - ) - continue - pack_ok = True - for key, value in parsed.items(): parsed_data.update({key + str(pack): value}) From c3bfad0ea6c1fe1be6f2b53d6c1bc5fb2c519d93 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 13:46:09 +0200 Subject: [PATCH 07/10] add pack sensors if available --- custom_components/bluetti_bt/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index 4b621b0..f437837 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -57,7 +57,7 @@ async def async_setup_entry( all_fields.update({name+str(pack): field}) for field_key, field_config in all_fields.items(): - if bluetti_device.has_field(field_key): + if bluetti_device.has_field(field_key) or (len(bluetti_device.pack_polling_commands) > 0 and field_key.startswith("pack_")): category = None if field_config.setter is True or field_key in DIAGNOSTIC_FIELDS: category = EntityCategory.DIAGNOSTIC From 6be2abaaf2836f3ff991ae39cdc3709be4d62204 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 14:23:07 +0200 Subject: [PATCH 08/10] fix pack polling --- .../base_devices/BluettiDevice.py | 2 +- .../base_devices/ProtocolV1Device.py | 2 +- .../bluetti_bt_lib/bluetooth/device_reader.py | 39 ++++--------------- .../bluetti_bt_lib/devices/ac200l.py | 2 +- .../bluetti_bt_lib/devices/ac200m.py | 2 +- .../bluetti_bt_lib/devices/ac300.py | 2 +- .../bluetti_bt_lib/devices/ac500.py | 2 +- .../bluetti_bt_lib/devices/ep500.py | 2 +- .../bluetti_bt_lib/devices/ep500p.py | 2 +- custom_components/bluetti_bt/sensor.py | 2 +- 10 files changed, 16 insertions(+), 41 deletions(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py index 44a16c3..0d1edae 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/base_devices/BluettiDevice.py @@ -45,7 +45,7 @@ class BluettiDevice: @property def pack_num_field(self) -> List[ReadHoldingRegisters]: - """The address 'range' of the pack num""" + """The address 'range' of the pack num result. Matches pack_num_result""" return [] def has_field(self, field: str): 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 91bd5a4..8160342 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 @@ -60,5 +60,5 @@ class ProtocolV1Device(BluettiDevice): @property def pack_num_field(self) -> List[ReadHoldingRegisters]: return [ - ReadHoldingRegisters(3006, 1), + ReadHoldingRegisters(96, 1), ] 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 a88beb0..e721ded 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 @@ -106,36 +106,11 @@ class DeviceReader: command = self.bluetti_device.build_setter_command( "pack_num", pack ) - await self._async_send_command(command) - - try: - # Read pack_num field - command = self.bluetti_device.pack_num_field[0] - body = command.parse_response( - await self._async_send_command(command) - ) - _LOGGER.debug("Raw data: %s", body) - parsed = self.bluetti_device.parse( - command.starting_address, body - ) - _LOGGER.debug("Parsed data: %s", parsed) - - # Check pack number - pack_number = parsed.get("pack_num") - if ( - not isinstance(pack_number, int) - or pack_number != pack - ): - _LOGGER.debug( - "Parsed pack_num(%s) does not match expected '%s', Skipping pack (maybe you don't have this connected)", - pack_number, - pack, - ) - continue - - except ParseError: - _LOGGER.warning("Got a pack parse exception, Skipping pack") - continue + body = command.parse_response( + await self._async_send_command(command) + ) + _LOGGER.debug("Raw data set: %s", body) + # b'\x00\x02' b'\x00\x03' here (seems like result) for command in pack_commands: # Request & parse result for each pack @@ -198,9 +173,9 @@ class DeviceReader: return cast(bytes, res) except TimeoutError: - _LOGGER.warning("Polling single command timed out") + _LOGGER.debug("Polling single command timed out") except ModbusError as err: - _LOGGER.warning( + _LOGGER.debug( "Got an invalid request error for %s: %s", command, 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 67be43d..f4c16b5 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py @@ -24,7 +24,7 @@ class AC200L(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 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 aa574b1..fb0d97c 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200m.py @@ -24,7 +24,7 @@ class AC200M(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 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 b8fcb64..e7a9303 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac300.py @@ -30,7 +30,7 @@ class AC300(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 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 f4ac5ff..3159dde 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac500.py @@ -30,7 +30,7 @@ class AC500(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 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 3bf7192..1554ce9 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500.py @@ -30,7 +30,7 @@ class EP500(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 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 e3748ae..ba19241 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ep500p.py @@ -30,7 +30,7 @@ class EP500P(ProtocolV1Device): # Battery packs self.struct.add_uint_field("pack_num_max", 91) # internal self.struct.add_decimal_field("total_battery_voltage", 92, 1) - self.struct.add_uint_field("pack_num", 96) # internal + self.struct.add_uint_field("pack_num_result", 96) # internal 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 diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index f437837..ba61abe 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -156,7 +156,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): response_data = self.coordinator.data.get(self._response_key) if response_data is None: - _LOGGER.warning("No data for available for (%s)", self._response_key) + _LOGGER.debug("No data for available for (%s)", self._response_key) self._attr_available = False self.async_write_ha_state() return From 206099ab64e96165bf7b651032fd244693b67f2a Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 14:23:43 +0200 Subject: [PATCH 09/10] increase version number --- custom_components/bluetti_bt/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/bluetti_bt/manifest.json b/custom_components/bluetti_bt/manifest.json index be3733e..0afaf91 100644 --- a/custom_components/bluetti_bt/manifest.json +++ b/custom_components/bluetti_bt/manifest.json @@ -23,5 +23,5 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/Patrick762/hassio-bluetti-bt/issues", "requirements": [], - "version": "0.0.24" + "version": "0.0.25" } From 12a59346e968617ca0e60a365b21365eb4a65413 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Fri, 31 May 2024 16:59:46 +0200 Subject: [PATCH 10/10] fix device type info --- .../bluetti_bt_lib/utils/device_builder.py | 2 +- custom_components/bluetti_bt/config_flow.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/custom_components/bluetti_bt/bluetti_bt_lib/utils/device_builder.py b/custom_components/bluetti_bt/bluetti_bt_lib/utils/device_builder.py index e0e56c7..ed912b2 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/utils/device_builder.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/utils/device_builder.py @@ -53,6 +53,6 @@ def build_device(address: str, name: str): def get_type_by_bt_name(bt_name: str): match = DEVICE_NAME_RE.match(bt_name) - if match is None or len(match) < 2: + if match is None: return "Unknown" return match[1] diff --git a/custom_components/bluetti_bt/config_flow.py b/custom_components/bluetti_bt/config_flow.py index 0d20d13..545ff4e 100644 --- a/custom_components/bluetti_bt/config_flow.py +++ b/custom_components/bluetti_bt/config_flow.py @@ -14,7 +14,7 @@ from homeassistant.components.bluetooth import ( BluetoothServiceInfoBleak, async_discovered_service_info, ) -from homeassistant.const import CONF_ADDRESS, CONF_NAME +from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_TYPE from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import selector @@ -48,11 +48,20 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(discovery_info.address) self._abort_if_unique_id_configured() + if isinstance(discovery_info.name, str): + name = re.sub("[^A-Z0-9]+", "", discovery_info.name) + discovery_info.manufacturer_data = { + CONF_TYPE: get_type_by_bt_name(name) + } + # Get device type if needed if isinstance(discovery_info.name, str) and discovery_info.name.startswith("PBOX"): bleak_device = BleakClient(discovery_info.device) device_type = await recognize_device(bleak_device, self.hass.loop.create_future) _LOGGER.info("Device identified as %s", device_type) + discovery_info.manufacturer_data = { + CONF_TYPE: device_type.strip(), + } discovery_info.name = discovery_info.name.replace("PBOX", device_type.strip()) self._discovery_info = discovery_info @@ -76,6 +85,7 @@ class BluettiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data={ CONF_ADDRESS: discovery_info.address, CONF_NAME: name, + CONF_TYPE: discovery_info.manufacturer_data.get(CONF_TYPE, "Unknown"), }, )