From b37276ec520bb01e08b02bebba2502c0746ad3c2 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sun, 2 Jun 2024 13:12:43 +0200 Subject: [PATCH 1/5] updated ac200l sensors based on feedback --- .../bluetti_bt/bluetti_bt_lib/devices/ac200l.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 f4c16b5..e1d5fa6 100644 --- a/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py +++ b/custom_components/bluetti_bt/bluetti_bt_lib/devices/ac200l.py @@ -13,17 +13,17 @@ class AC200L(ProtocolV1Device): # Details 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_ac_voltage", 71, 1, multiplier=10) self.struct.add_decimal_field("internal_current_one", 72, 1) self.struct.add_uint_field("internal_power_one", 73) - self.struct.add_decimal_field("internal_ac_frequency", 74, 2) - self.struct.add_uint_field("internal_dc_input_voltage", 86) - self.struct.add_decimal_field("internal_dc_input_power", 87, 1) + self.struct.add_decimal_field("internal_ac_frequency", 74, 2, multiplier=10) + self.struct.add_uint_field("internal_dc_input_voltage", 86, multiplier=.1) + self.struct.add_decimal_field("internal_dc_input_power", 87, 1, multiplier=10) self.struct.add_decimal_field("internal_dc_input_current", 88, 2) # 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_decimal_field("total_battery_voltage", 92, 1, multiplier=.1) 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) From 09a9112279a7814daa19d5c00e8e6fc49898ae0f Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sun, 2 Jun 2024 13:48:45 +0200 Subject: [PATCH 2/5] check pack_num after setting it --- .../bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py | 7 ++++++- 1 file changed, 6 insertions(+), 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 e721ded..57bf44c 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 @@ -110,7 +110,12 @@ class DeviceReader: await self._async_send_command(command) ) _LOGGER.debug("Raw data set: %s", body) - # b'\x00\x02' b'\x00\x03' here (seems like result) + + # Check set pack_num + set_pack = int.from_bytes(body, byteorder='big') + if set_pack is not pack: + _LOGGER.warning("Pack polling failed (pack_num %i doesn't match expected %i)", set_pack, pack) + continue for command in pack_commands: # Request & parse result for each pack From 4d54e048c5899297326fcffdb0540c5d263816a0 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sun, 2 Jun 2024 13:49:18 +0200 Subject: [PATCH 3/5] ignore packs not connected --- .../bluetti_bt/bluetti_bt_lib/bluetooth/device_reader.py | 4 +++- 1 file changed, 3 insertions(+), 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 57bf44c..6dd6fd0 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 @@ -129,7 +129,9 @@ class DeviceReader: _LOGGER.debug("Parsed data: %s", parsed) for key, value in parsed.items(): - parsed_data.update({key + str(pack): value}) + # Ignore likely unavailable pack data + if value != 0: + parsed_data.update({key + str(pack): value}) except ParseError: _LOGGER.warning("Got a parse exception...") From ece1fc41292bfb2ab9497ef0e9ed8a93af24b0d6 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sun, 2 Jun 2024 13:58:21 +0200 Subject: [PATCH 4/5] only set unavailable after 5 retries --- custom_components/bluetti_bt/binary_sensor.py | 39 +++++++++++++++---- custom_components/bluetti_bt/sensor.py | 36 ++++++++++++----- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/custom_components/bluetti_bt/binary_sensor.py b/custom_components/bluetti_bt/binary_sensor.py index 0e09ee7..5bb013e 100644 --- a/custom_components/bluetti_bt/binary_sensor.py +++ b/custom_components/bluetti_bt/binary_sensor.py @@ -96,6 +96,27 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): def available(self) -> bool: """Return if entity is available.""" return self._attr_available + + def _set_available(self): + """Set sensor as available.""" + self._attr_available = True + self._unavailable_counter = 0 + self._attr_extra_state_attributes = {} + self.async_write_ha_state() + + def _set_unavailable(self, cause: str = "Unknown"): + """Set sensor as unavailable.""" + self._unavailable_counter += 1 + + self._attr_extra_state_attributes = { + "unavailable_counter": self._unavailable_counter, + "unavailable_cause": cause, + } + + if self._unavailable_counter >= 5: + self._attr_available = False + + self.async_write_ha_state() @callback def _handle_coordinator_update(self) -> None: @@ -103,20 +124,25 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): if self.coordinator.reader.persistent_conn and not self.coordinator.reader.client.is_connected: return + + if self.coordinator.data is None: + _LOGGER.debug( + "Data from coordinator is None", + ) + self._set_unavailable("Data is None") + return _LOGGER.debug("Updating state of %s", unique_id_loggable(self._attr_unique_id)) if not isinstance(self.coordinator.data, dict): _LOGGER.debug( "Invalid data from coordinator (binary_sensor.%s)", unique_id_loggable(self._attr_unique_id) ) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("Invalid data") return response_data = self.coordinator.data.get(self._response_key) if response_data is None: - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("No data") return if not isinstance(response_data, bool): @@ -125,10 +151,9 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): unique_id_loggable(self._attr_unique_id), response_data, ) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("Invalid data type") return - self._attr_available = True + self._set_available() self._attr_is_on = self.coordinator.data[self._response_key] is True self.async_write_ha_state() diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index ba61abe..268460f 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -114,6 +114,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): e_name = f"{device_info.get('name')} {name}" self._address = address self._response_key = response_key + self._unavailable_counter = 0 self._attr_device_info = device_info self._attr_name = name @@ -130,6 +131,27 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): """Return if entity is available.""" return self._attr_available + def _set_available(self): + """Set sensor as available.""" + self._attr_available = True + self._unavailable_counter = 0 + self._attr_extra_state_attributes = {} + self.async_write_ha_state() + + def _set_unavailable(self, cause: str = "Unknown"): + """Set sensor as unavailable.""" + self._unavailable_counter += 1 + + self._attr_extra_state_attributes = { + "unavailable_counter": self._unavailable_counter, + "unavailable_cause": cause, + } + + if self._unavailable_counter >= 5: + self._attr_available = False + + self.async_write_ha_state() + @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" @@ -141,8 +163,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): _LOGGER.debug( "Data from coordinator is None", ) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("Data is None") return _LOGGER.debug("Updating state of %s", unique_id_loggable(self._attr_unique_id)) @@ -150,15 +171,13 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): _LOGGER.warning( "Invalid data from coordinator (sensor.%s)", unique_id_loggable(self._attr_unique_id) ) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("Invalid data") return response_data = self.coordinator.data.get(self._response_key) if response_data is None: _LOGGER.debug("No data for available for (%s)", self._response_key) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("No data") return if ( @@ -174,11 +193,10 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): response_data, type(response_data), ) - self._attr_available = False - self.async_write_ha_state() + self._set_unavailable("Invalid data type") return - self._attr_available = True + self._set_available() # Different for enum and numeric if ( From 3169ee182eeb007e40938cced4c8c98454bb5bf7 Mon Sep 17 00:00:00 2001 From: Patrick762 Date: Sun, 2 Jun 2024 14:03:02 +0200 Subject: [PATCH 5/5] 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 e7030ff..c81c315 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.1.0" + "version": "0.1.1" }