diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 4b79ef3df1b..b848bc0f02c 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -10,7 +10,6 @@ import logging import re from typing import Any -import aiohttp.client_exceptions import evohomeasync import evohomeasync2 import voluptuous as vol @@ -144,7 +143,7 @@ def _handle_exception(err) -> None: try: raise err - except evohomeasync2.AuthenticationError: + except evohomeasync2.AuthenticationFailed: _LOGGER.error( ( "Failed to authenticate with the vendor's server. Check your username" @@ -155,19 +154,18 @@ def _handle_exception(err) -> None: err, ) - except aiohttp.ClientConnectionError: - # this appears to be a common occurrence with the vendor's servers - _LOGGER.warning( - ( - "Unable to connect with the vendor's server. " - "Check your network and the vendor's service status page. " - "Message is: %s" - ), - err, - ) + except evohomeasync2.RequestFailed: + if err.status is None: + _LOGGER.warning( + ( + "Unable to connect with the vendor's server. " + "Check your network and the vendor's service status page. " + "Message is: %s" + ), + err, + ) - except aiohttp.ClientResponseError: - if err.status == HTTPStatus.SERVICE_UNAVAILABLE: + elif err.status == HTTPStatus.SERVICE_UNAVAILABLE: _LOGGER.warning( "The vendor says their server is currently unavailable. " "Check the vendor's service status page" @@ -219,7 +217,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: try: await client_v2.login() - except (aiohttp.ClientError, evohomeasync2.AuthenticationError) as err: + except evohomeasync2.AuthenticationFailed as err: _handle_exception(err) return False finally: @@ -452,7 +450,7 @@ class EvoBroker: """Call a client API and update the broker state if required.""" try: result = await api_function - except (aiohttp.ClientError, evohomeasync2.AuthenticationError) as err: + except evohomeasync2.EvohomeError as err: _handle_exception(err) return @@ -475,15 +473,27 @@ class EvoBroker: try: temps = list(await self.client_v1.temperatures(force_refresh=True)) - except aiohttp.ClientError as err: + except evohomeasync.InvalidSchema as exc: + _LOGGER.warning( + ( + "Unable to obtain high-precision temperatures. " + "It appears the JSON schema is not as expected, " + "so the high-precision feature will be disabled until next restart." + "Message is: %s" + ), + exc, + ) + self.temps = self.client_v1 = None + + except evohomeasync.EvohomeError as exc: _LOGGER.warning( ( "Unable to obtain the latest high-precision temperatures. " "Check your network and the vendor's service status page. " - "Proceeding with low-precision temperatures. " + "Proceeding without high-precision temperatures for now. " "Message is: %s" ), - err, + exc, ) self.temps = None # these are now stale, will fall back to v2 temps @@ -513,10 +523,11 @@ class EvoBroker: else: self.temps = {str(i["id"]): i["temp"] for i in temps} - _LOGGER.debug("Temperatures = %s", self.temps) + finally: + if session_id != get_session_id(self.client_v1): + await self.save_auth_tokens() - if session_id != get_session_id(self.client_v1): - await self.save_auth_tokens() + _LOGGER.debug("Temperatures = %s", self.temps) async def _update_v2_api_state(self, *args, **kwargs) -> None: """Get the latest modes, temperatures, setpoints of a Location.""" @@ -524,8 +535,8 @@ class EvoBroker: loc_idx = self.params[CONF_LOCATION_IDX] try: - status = await self.client.locations[loc_idx].status() - except (aiohttp.ClientError, evohomeasync2.AuthenticationError) as err: + status = await self.client.locations[loc_idx].refresh_status() + except evohomeasync2.EvohomeError as err: _handle_exception(err) else: async_dispatcher_send(self.hass, DOMAIN) @@ -542,11 +553,14 @@ class EvoBroker: operating mode of the Controller and the current temp of its children (e.g. Zones, DHW controller). """ - if self.client_v1: - await self._update_v1_api_temps() - await self._update_v2_api_state() + if self.client_v1: + try: + await self._update_v1_api_temps() + except evohomeasync.EvohomeError: + self.temps = None # these are now stale, will fall back to v2 temps + class EvoDevice(Entity): """Base for any evohome device. @@ -618,11 +632,13 @@ class EvoChild(EvoDevice): @property def current_temperature(self) -> float | None: """Return the current temperature of a Zone.""" - if ( - self._evo_broker.temps - and self._evo_broker.temps[self._evo_device.zoneId] != 128 - ): - return self._evo_broker.temps[self._evo_device.zoneId] + if self._evo_device.TYPE == "domesticHotWater": + dev_id = self._evo_device.dhwId + else: + dev_id = self._evo_device.zoneId + + if self._evo_broker.temps and self._evo_broker.temps[dev_id] is not None: + return self._evo_broker.temps[dev_id] if self._evo_device.temperatureStatus["isAvailable"]: return self._evo_device.temperatureStatus["temperature"] @@ -695,7 +711,7 @@ class EvoChild(EvoDevice): async def _update_schedule(self) -> None: """Get the latest schedule, if any.""" self._schedule = await self._evo_broker.call_client_api( - self._evo_device.schedule(), update_state=False + self._evo_device.get_schedule(), update_state=False ) _LOGGER.debug("Schedule['%s'] = %s", self.name, self._schedule) diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index 3bee1d6062e..fb608262a7d 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -167,9 +167,7 @@ class EvoZone(EvoChild, EvoClimateEntity): async def async_zone_svc_request(self, service: str, data: dict[str, Any]) -> None: """Process a service request (setpoint override) for a zone.""" if service == SVC_RESET_ZONE_OVERRIDE: - await self._evo_broker.call_client_api( - self._evo_device.cancel_temp_override() - ) + await self._evo_broker.call_client_api(self._evo_device.reset_mode()) return # otherwise it is SVC_SET_ZONE_OVERRIDE @@ -264,18 +262,14 @@ class EvoZone(EvoChild, EvoClimateEntity): self._evo_device.set_temperature(self.min_temp, until=None) ) else: # HVACMode.HEAT - await self._evo_broker.call_client_api( - self._evo_device.cancel_temp_override() - ) + await self._evo_broker.call_client_api(self._evo_device.reset_mode()) async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode; if None, then revert to following the schedule.""" evo_preset_mode = HA_PRESET_TO_EVO.get(preset_mode, EVO_FOLLOW) if evo_preset_mode == EVO_FOLLOW: - await self._evo_broker.call_client_api( - self._evo_device.cancel_temp_override() - ) + await self._evo_broker.call_client_api(self._evo_device.reset_mode()) return temperature = self._evo_device.setpointStatus["targetHeatTemperature"] @@ -352,7 +346,7 @@ class EvoController(EvoClimateEntity): """Set a Controller to any of its native EVO_* operating modes.""" until = dt_util.as_utc(until) if until else None await self._evo_broker.call_client_api( - self._evo_tcs.set_status(mode, until=until) + self._evo_tcs.set_mode(mode, until=until) ) @property diff --git a/homeassistant/components/evohome/manifest.json b/homeassistant/components/evohome/manifest.json index 641833ef06a..3cf07dfdfc4 100644 --- a/homeassistant/components/evohome/manifest.json +++ b/homeassistant/components/evohome/manifest.json @@ -5,5 +5,5 @@ "documentation": "https://www.home-assistant.io/integrations/evohome", "iot_class": "cloud_polling", "loggers": ["evohomeasync", "evohomeasync2"], - "requirements": ["evohome-async==0.3.15"] + "requirements": ["evohome-async==0.4.4"] } diff --git a/homeassistant/components/evohome/water_heater.py b/homeassistant/components/evohome/water_heater.py index 87c0a8a1ecd..5d49e9b46ec 100644 --- a/homeassistant/components/evohome/water_heater.py +++ b/homeassistant/components/evohome/water_heater.py @@ -46,9 +46,10 @@ async def async_setup_platform( _LOGGER.debug( "Adding: DhwController (%s), id=%s", - broker.tcs.hotwater.zone_type, - broker.tcs.hotwater.zoneId, + broker.tcs.hotwater.TYPE, + broker.tcs.hotwater.dhwId, ) + new_entity = EvoDHW(broker, broker.tcs.hotwater) async_add_entities([new_entity], update_before_add=True) @@ -95,7 +96,7 @@ class EvoDHW(EvoChild, WaterHeaterEntity): Except for Auto, the mode is only until the next SetPoint. """ if operation_mode == STATE_AUTO: - await self._evo_broker.call_client_api(self._evo_device.set_dhw_auto()) + await self._evo_broker.call_client_api(self._evo_device.reset_mode()) else: await self._update_schedule() until = dt_util.parse_datetime(self.setpoints.get("next_sp_from", "")) @@ -103,28 +104,28 @@ class EvoDHW(EvoChild, WaterHeaterEntity): if operation_mode == STATE_ON: await self._evo_broker.call_client_api( - self._evo_device.set_dhw_on(until=until) + self._evo_device.set_on(until=until) ) else: # STATE_OFF await self._evo_broker.call_client_api( - self._evo_device.set_dhw_off(until=until) + self._evo_device.set_off(until=until) ) async def async_turn_away_mode_on(self) -> None: """Turn away mode on.""" - await self._evo_broker.call_client_api(self._evo_device.set_dhw_off()) + await self._evo_broker.call_client_api(self._evo_device.set_off()) async def async_turn_away_mode_off(self) -> None: """Turn away mode off.""" - await self._evo_broker.call_client_api(self._evo_device.set_dhw_auto()) + await self._evo_broker.call_client_api(self._evo_device.reset_mode()) async def async_turn_on(self): """Turn on.""" - await self._evo_broker.call_client_api(self._evo_device.set_dhw_on()) + await self._evo_broker.call_client_api(self._evo_device.set_on()) async def async_turn_off(self): """Turn off.""" - await self._evo_broker.call_client_api(self._evo_device.set_dhw_off()) + await self._evo_broker.call_client_api(self._evo_device.set_off()) async def async_update(self) -> None: """Get the latest state data for a DHW controller.""" diff --git a/requirements_all.txt b/requirements_all.txt index a5b22950536..384b86fece4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -785,7 +785,7 @@ eufylife-ble-client==0.1.8 # evdev==1.6.1 # homeassistant.components.evohome -evohome-async==0.3.15 +evohome-async==0.4.4 # homeassistant.components.faa_delays faadelays==2023.9.1