diff --git a/custom_components/bluetti_bt/binary_sensor.py b/custom_components/bluetti_bt/binary_sensor.py index ab235d4..ca6457a 100644 --- a/custom_components/bluetti_bt/binary_sensor.py +++ b/custom_components/bluetti_bt/binary_sensor.py @@ -100,6 +100,11 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): self._attr_unique_id = get_unique_id(e_name) self._attr_entity_category = category + @property + def available(self) -> bool: + """Return if entity is available.""" + return self._attr_available + @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" @@ -113,11 +118,13 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): "Invalid data from coordinator (binary_sensor.%s)", unique_id_loggable(self._attr_unique_id) ) self._attr_available = False + self.async_write_ha_state() return response_data = self.coordinator.data.get(self._response_key) if response_data is None: self._attr_available = False + self.async_write_ha_state() return if not isinstance(response_data, bool): @@ -127,6 +134,7 @@ class BluettiBinarySensor(CoordinatorEntity, BinarySensorEntity): response_data, ) self._attr_available = False + self.async_write_ha_state() return self._attr_available = True 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 c8cc38b..0e04564 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 @@ -144,7 +144,7 @@ class DeviceReader: _LOGGER.warning("Got a parse exception...") except TimeoutError: - _LOGGER.error("Polling timed out") + _LOGGER.warning("Polling timed out. Trying again later") return None except BleakError as err: _LOGGER.error("Bleak error: %s", err) @@ -153,10 +153,18 @@ class DeviceReader: # Disconnect if connection not persistant if not self.persistent_conn: if self.has_notifier: - await self.client.stop_notify(NOTIFY_UUID) + try: + await self.client.stop_notify(NOTIFY_UUID) + except: + # Ignore errors here + pass self.has_notifier = False await self.client.disconnect() + # Check if dict is empty + if not parsed_data: + return None + return parsed_data async def _async_send_command(self, command: ReadHoldingRegisters) -> bytes: @@ -187,10 +195,8 @@ class DeviceReader: err, ) except (BadConnectionError, BleakError) as err: - _LOGGER.warning( - "Needed to disconnect due to error: %s (This can also be the case if you used device controls)", - err, - ) + # Ignore other errors + pass # caught an exception, return empty bytes object return bytes() diff --git a/custom_components/bluetti_bt/coordinator.py b/custom_components/bluetti_bt/coordinator.py index c85b4c8..de8bd40 100644 --- a/custom_components/bluetti_bt/coordinator.py +++ b/custom_components/bluetti_bt/coordinator.py @@ -42,6 +42,8 @@ class PollingCoordinator(DataUpdateCoordinator): update_interval=timedelta(seconds=polling_interval), ) + self.address = address + # Create client self.logger.debug("Creating client") device = bluetooth.async_ble_device_from_address(hass, address) @@ -67,4 +69,10 @@ class PollingCoordinator(DataUpdateCoordinator): so entities can quickly look up their data. """ + # Check if device is connected + if bluetooth.async_address_present(self.hass, self.address, connectable=True) is False: + self.logger.warning("Device not connected") + self.last_update_success = False + return None + return await self.reader.read_data() diff --git a/custom_components/bluetti_bt/sensor.py b/custom_components/bluetti_bt/sensor.py index 9390b8e..4b621b0 100644 --- a/custom_components/bluetti_bt/sensor.py +++ b/custom_components/bluetti_bt/sensor.py @@ -125,6 +125,11 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): self._attr_entity_category = category self._options = options + @property + def available(self) -> bool: + """Return if entity is available.""" + return self._attr_available + @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" @@ -133,10 +138,11 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): return if self.coordinator.data is None: - _LOGGER.warning( - "Data from coordinator is None. Skipping update for %s", - unique_id_loggable(self._attr_unique_id) + _LOGGER.debug( + "Data from coordinator is None", ) + self._attr_available = False + self.async_write_ha_state() return _LOGGER.debug("Updating state of %s", unique_id_loggable(self._attr_unique_id)) @@ -145,12 +151,14 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): "Invalid data from coordinator (sensor.%s)", unique_id_loggable(self._attr_unique_id) ) self._attr_available = False + self.async_write_ha_state() return 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) self._attr_available = False + self.async_write_ha_state() return if ( @@ -167,6 +175,7 @@ class BluettiSensor(CoordinatorEntity, SensorEntity): type(response_data), ) self._attr_available = False + self.async_write_ha_state() return self._attr_available = True diff --git a/custom_components/bluetti_bt/switch.py b/custom_components/bluetti_bt/switch.py index 4481fdf..11efc32 100644 --- a/custom_components/bluetti_bt/switch.py +++ b/custom_components/bluetti_bt/switch.py @@ -108,6 +108,11 @@ class BluettiSwitch(CoordinatorEntity, SwitchEntity): self._attr_entity_category = category self._attr_device_class = SwitchDeviceClass.OUTLET + @property + def available(self) -> bool: + """Return if entity is available.""" + return self._attr_available + @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" @@ -117,15 +122,17 @@ class BluettiSwitch(CoordinatorEntity, SwitchEntity): _LOGGER.debug("Updating state of %s", unique_id_loggable(self._attr_unique_id)) if not isinstance(self.coordinator.data, dict): - _LOGGER.error( + _LOGGER.debug( "Invalid data from coordinator (switch.%s)", unique_id_loggable(self._attr_unique_id) ) self._attr_available = False + self.async_write_ha_state() return response_data = self.coordinator.data.get(self._response_key) if response_data is None: self._attr_available = False + self.async_write_ha_state() return if not isinstance(response_data, bool): @@ -135,6 +142,7 @@ class BluettiSwitch(CoordinatorEntity, SwitchEntity): response_data, ) self._attr_available = False + self.async_write_ha_state() return self._attr_available = True