Add asyncio locks to screenlogic api access points (#48457)

This commit is contained in:
Kevin Worrel 2021-03-29 16:27:17 -07:00 committed by GitHub
parent c1d5638739
commit 42a060ad33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 15 deletions

View file

@ -60,8 +60,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
_LOGGER.error("Error while connecting to the gateway %s: %s", connect_info, ex) _LOGGER.error("Error while connecting to the gateway %s: %s", connect_info, ex)
raise ConfigEntryNotReady from ex raise ConfigEntryNotReady from ex
# The api library uses a shared socket connection and does not handle concurrent
# requests very well.
api_lock = asyncio.Lock()
coordinator = ScreenlogicDataUpdateCoordinator( coordinator = ScreenlogicDataUpdateCoordinator(
hass, config_entry=entry, gateway=gateway hass, config_entry=entry, gateway=gateway, api_lock=api_lock
) )
device_data = defaultdict(list) device_data = defaultdict(list)
@ -127,10 +131,11 @@ async def async_update_listener(hass: HomeAssistant, entry: ConfigEntry):
class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator): class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage the data update for the Screenlogic component.""" """Class to manage the data update for the Screenlogic component."""
def __init__(self, hass, *, config_entry, gateway): def __init__(self, hass, *, config_entry, gateway, api_lock):
"""Initialize the Screenlogic Data Update Coordinator.""" """Initialize the Screenlogic Data Update Coordinator."""
self.config_entry = config_entry self.config_entry = config_entry
self.gateway = gateway self.gateway = gateway
self.api_lock = api_lock
self.screenlogic_data = {} self.screenlogic_data = {}
interval = timedelta( interval = timedelta(
seconds=config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) seconds=config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
@ -145,7 +150,8 @@ class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator):
async def _async_update_data(self): async def _async_update_data(self):
"""Fetch data from the Screenlogic gateway.""" """Fetch data from the Screenlogic gateway."""
try: try:
await self.hass.async_add_executor_job(self.gateway.update) async with self.api_lock:
await self.hass.async_add_executor_job(self.gateway.update)
except ScreenLogicError as error: except ScreenLogicError as error:
raise UpdateFailed(error) from error raise UpdateFailed(error) from error
return self.gateway.get_data() return self.gateway.get_data()

View file

@ -138,9 +138,12 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity):
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}") raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
if await self.hass.async_add_executor_job( async with self.coordinator.api_lock:
self.gateway.set_heat_temp, int(self._data_key), int(temperature) success = await self.hass.async_add_executor_job(
): self.gateway.set_heat_temp, int(self._data_key), int(temperature)
)
if success:
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
else: else:
raise HomeAssistantError( raise HomeAssistantError(
@ -153,9 +156,13 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity):
mode = HEAT_MODE.OFF mode = HEAT_MODE.OFF
else: else:
mode = HEAT_MODE.NUM_FOR_NAME[self.preset_mode] mode = HEAT_MODE.NUM_FOR_NAME[self.preset_mode]
if await self.hass.async_add_executor_job(
self.gateway.set_heat_mode, int(self._data_key), int(mode) async with self.coordinator.api_lock:
): success = await self.hass.async_add_executor_job(
self.gateway.set_heat_mode, int(self._data_key), int(mode)
)
if success:
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
else: else:
raise HomeAssistantError( raise HomeAssistantError(
@ -168,9 +175,13 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity):
self._last_preset = mode = HEAT_MODE.NUM_FOR_NAME[preset_mode] self._last_preset = mode = HEAT_MODE.NUM_FOR_NAME[preset_mode]
if self.hvac_mode == HVAC_MODE_OFF: if self.hvac_mode == HVAC_MODE_OFF:
return return
if await self.hass.async_add_executor_job(
self.gateway.set_heat_mode, int(self._data_key), int(mode) async with self.coordinator.api_lock:
): success = await self.hass.async_add_executor_job(
self.gateway.set_heat_mode, int(self._data_key), int(mode)
)
if success:
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
else: else:
raise HomeAssistantError( raise HomeAssistantError(

View file

@ -44,9 +44,12 @@ class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity):
return await self._async_set_circuit(ON_OFF.OFF) return await self._async_set_circuit(ON_OFF.OFF)
async def _async_set_circuit(self, circuit_value) -> None: async def _async_set_circuit(self, circuit_value) -> None:
if await self.hass.async_add_executor_job( async with self.coordinator.api_lock:
self.gateway.set_circuit, self._data_key, circuit_value success = await self.hass.async_add_executor_job(
): self.gateway.set_circuit, self._data_key, circuit_value
)
if success:
_LOGGER.debug("Turn %s %s", self._data_key, circuit_value) _LOGGER.debug("Turn %s %s", self._data_key, circuit_value)
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
else: else: