Allow some failures of updates before reporting an error

This commit is contained in:
Luke Bonaccorsi 2023-09-11 12:08:05 +01:00
parent 7f5cc8c30f
commit 949edc6b41
2 changed files with 72 additions and 47 deletions

View File

@ -722,8 +722,8 @@ class TuyaDevice:
self.last_pong = time.time() self.last_pong = time.time()
async def async_update_state(self, state_message, _): async def async_update_state(self, state_message, _):
_LOGGER.info("Received updated state {}: {}").format(self, self._dps)
self._dps.update(state_message.payload["dps"]) self._dps.update(state_message.payload["dps"])
_LOGGER.info("Received updated state {}: {}".format(self, self._dps))
@property @property
def state(self): def state(self):
@ -757,15 +757,16 @@ class TuyaDevice:
except Exception as e: except Exception as e:
if retries == 0: if retries == 0:
if isinstance(e, socket.error): if isinstance(e, socket.error):
_LOGGER.error("Connection to {} failed: {}".format(self, e))
self._dps["106"] = "CONNECTION_FAILED"
asyncio.ensure_future(self.async_disconnect()) asyncio.ensure_future(self.async_disconnect())
raise ConnectionException(
"Connection to {} failed: {}".format(self, e)
)
elif isinstance(e, asyncio.IncompleteReadError): elif isinstance(e, asyncio.IncompleteReadError):
_LOGGER.error("Incomplete read from: {} : {}".format(self, e)) raise InvalidMessage(
"Incomplete read from: {} : {}".format(self, e)
)
else: else:
_LOGGER.error("Failed to send data to {}".format(self)) raise TuyaException("Failed to send data to {}".format(self))
return
if isinstance(e, socket.error): if isinstance(e, socket.error):
_LOGGER.debug( _LOGGER.debug(
@ -775,8 +776,8 @@ class TuyaDevice:
) )
elif isinstance(e, asyncio.IncompleteReadError): elif isinstance(e, asyncio.IncompleteReadError):
_LOGGER.debug( _LOGGER.debug(
"Retrying send due to error.Incomplete read from: {} : {}".format( "Retrying send due to error. Incomplete read from: {} : {}. Partial data recieved: {}".format(
self, e self, e, e.partial
) )
) )
else: else:

View File

@ -55,6 +55,7 @@ from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
) )
from .tuyalocalapi import TuyaException
from .const import CONF_VACS, DOMAIN from .const import CONF_VACS, DOMAIN
from .errors import getErrorMessage from .errors import getErrorMessage
@ -85,6 +86,7 @@ ATTR_MODE = "mode"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REFRESH_RATE = 20 REFRESH_RATE = 20
SCAN_INTERVAL = timedelta(seconds=REFRESH_RATE) SCAN_INTERVAL = timedelta(seconds=REFRESH_RATE)
UPDATE_RETRIES = 3
class TUYA_CODES(StrEnum): class TUYA_CODES(StrEnum):
@ -99,6 +101,7 @@ class TUYA_CODES(StrEnum):
DO_NOT_DISTURB = "107" DO_NOT_DISTURB = "107"
BOOST_IQ = "118" BOOST_IQ = "118"
TUYA_CONSUMABLES_CODES = ["142", "116"] TUYA_CONSUMABLES_CODES = ["142", "116"]
@ -191,11 +194,20 @@ class RoboVacEntity(StateVacuumEntity):
def state(self) -> str | None: def state(self) -> str | None:
if self.tuya_state is None: if self.tuya_state is None:
return STATE_UNAVAILABLE return STATE_UNAVAILABLE
elif type(self.error_code) is not None and self.error_code and self.error_code not in [ elif (
0, type(self.error_code) is not None
"no_error", and self.error_code
]: and self.error_code
_LOGGER.debug("State changed to error. Error message: {}".format(getErrorMessage(self.error_code))) not in [
0,
"no_error",
]
):
_LOGGER.debug(
"State changed to error. Error message: {}".format(
getErrorMessage(self.error_code)
)
)
return STATE_ERROR return STATE_ERROR
elif self.tuya_state == "Charging" or self.tuya_state == "completed": elif self.tuya_state == "Charging" or self.tuya_state == "completed":
return STATE_DOCKED return STATE_DOCKED
@ -254,6 +266,8 @@ class RoboVacEntity(StateVacuumEntity):
self._attr_ip_address = item[CONF_IP_ADDRESS] self._attr_ip_address = item[CONF_IP_ADDRESS]
self._attr_access_token = item[CONF_ACCESS_TOKEN] self._attr_access_token = item[CONF_ACCESS_TOKEN]
self.update_failures = 0
try: try:
self.vacuum = RoboVac( self.vacuum = RoboVac(
device_id=self.unique_id, device_id=self.unique_id,
@ -295,40 +309,50 @@ class RoboVacEntity(StateVacuumEntity):
self.error_code = "IP_ADDRESS" self.error_code = "IP_ADDRESS"
return return
await self.vacuum.async_get() try:
self.tuyastatus = self.vacuum._dps await self.vacuum.async_get()
# for 15C self.update_failures = 0
self._attr_battery_level = self.tuyastatus.get(TUYA_CODES.BATTERY_LEVEL) self.tuyastatus = self.vacuum._dps
self.tuya_state = self.tuyastatus.get(TUYA_CODES.STATE)
self.error_code = self.tuyastatus.get(TUYA_CODES.ERROR_CODE) # for 15C
self._attr_mode = self.tuyastatus.get(TUYA_CODES.MODE) self._attr_battery_level = self.tuyastatus.get(TUYA_CODES.BATTERY_LEVEL)
self._attr_fan_speed = self.tuyastatus.get(TUYA_CODES.FAN_SPEED) self.tuya_state = self.tuyastatus.get(TUYA_CODES.STATE)
if self.fan_speed == "No_suction": self.error_code = self.tuyastatus.get(TUYA_CODES.ERROR_CODE)
self._attr_fan_speed = "No Suction" self._attr_mode = self.tuyastatus.get(TUYA_CODES.MODE)
elif self.fan_speed == "Boost_IQ": self._attr_fan_speed = self.tuyastatus.get(TUYA_CODES.FAN_SPEED)
self._attr_fan_speed = "Boost IQ" if self.fan_speed == "No_suction":
elif self.fan_speed == "Quiet": self._attr_fan_speed = "No Suction"
self._attr_fan_speed = "Pure" elif self.fan_speed == "Boost_IQ":
# for G30 self._attr_fan_speed = "Boost IQ"
self._attr_cleaning_area = self.tuyastatus.get(TUYA_CODES.CLEANING_AREA) elif self.fan_speed == "Quiet":
self._attr_cleaning_time = self.tuyastatus.get(TUYA_CODES.CLEANING_TIME) self._attr_fan_speed = "Pure"
self._attr_auto_return = self.tuyastatus.get(TUYA_CODES.AUTO_RETURN) # for G30
self._attr_do_not_disturb = self.tuyastatus.get(TUYA_CODES.DO_NOT_DISTURB) self._attr_cleaning_area = self.tuyastatus.get(TUYA_CODES.CLEANING_AREA)
self._attr_boost_iq = self.tuyastatus.get(TUYA_CODES.BOOST_IQ) self._attr_cleaning_time = self.tuyastatus.get(TUYA_CODES.CLEANING_TIME)
# self.map_data = self.tuyastatus.get("121") self._attr_auto_return = self.tuyastatus.get(TUYA_CODES.AUTO_RETURN)
# self.erro_msg? = self.tuyastatus.get("124") self._attr_do_not_disturb = self.tuyastatus.get(TUYA_CODES.DO_NOT_DISTURB)
if self.robovac_supported & RoboVacEntityFeature.CONSUMABLES: self._attr_boost_iq = self.tuyastatus.get(TUYA_CODES.BOOST_IQ)
for CONSUMABLE_CODE in TUYA_CONSUMABLES_CODES: # self.map_data = self.tuyastatus.get("121")
if ( # self.erro_msg? = self.tuyastatus.get("124")
CONSUMABLE_CODE in self.tuyastatus if self.robovac_supported & RoboVacEntityFeature.CONSUMABLES:
and self.tuyastatus.get(CONSUMABLE_CODE) is not None for CONSUMABLE_CODE in TUYA_CONSUMABLES_CODES:
): if (
self._attr_consumables = ast.literal_eval( CONSUMABLE_CODE in self.tuyastatus
base64.b64decode(self.tuyastatus.get(CONSUMABLE_CODE)).decode( and self.tuyastatus.get(CONSUMABLE_CODE) is not None
"ascii" ):
) self._attr_consumables = ast.literal_eval(
)["consumable"]["duration"] base64.b64decode(
self.tuyastatus.get(CONSUMABLE_CODE)
).decode("ascii")
)["consumable"]["duration"]
except TuyaException as e:
self.update_failures += 1
_LOGGER.debug("Update errored. Current failure count: {}. Reason: {}".format(self.update_failures, e))
if self.update_failures == UPDATE_RETRIES:
self.update_failures = 0
self.error_code = "CONNECTION_FAILED"
raise e
async def async_locate(self, **kwargs): async def async_locate(self, **kwargs):
"""Locate the vacuum cleaner.""" """Locate the vacuum cleaner."""