Bump evohome-async to 0.4.9 (#103660)

pull/104832/head
David Bonnes 2023-11-30 17:17:34 +00:00 committed by GitHub
parent abc05451a2
commit f50cd5ab5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 63 deletions

View File

@ -190,14 +190,14 @@ def _handle_exception(err) -> None:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Create a (EMEA/EU-based) Honeywell TCC system."""
async def load_auth_tokens(store) -> tuple[dict, dict | None]:
async def load_auth_tokens(store) -> tuple[dict[str, str | dt], dict[str, str]]:
app_storage = await store.async_load()
tokens = dict(app_storage or {})
if tokens.pop(CONF_USERNAME, None) != config[DOMAIN][CONF_USERNAME]:
# any tokens won't be valid, and store might be corrupt
await store.async_save({})
return ({}, None)
return ({}, {})
# evohomeasync2 requires naive/local datetimes as strings
if tokens.get(ACCESS_TOKEN_EXPIRES) is not None and (
@ -205,7 +205,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
):
tokens[ACCESS_TOKEN_EXPIRES] = _dt_aware_to_naive(expires)
user_data = tokens.pop(USER_DATA, None)
user_data = tokens.pop(USER_DATA, {})
return (tokens, user_data)
store = Store[dict[str, Any]](hass, STORAGE_VER, STORAGE_KEY)
@ -214,7 +214,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
client_v2 = evohomeasync2.EvohomeClient(
config[DOMAIN][CONF_USERNAME],
config[DOMAIN][CONF_PASSWORD],
**tokens,
**tokens, # type: ignore[arg-type]
session=async_get_clientsession(hass),
)
@ -253,7 +253,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
client_v1 = evohomeasync.EvohomeClient(
client_v2.username,
client_v2.password,
user_data=user_data,
session_id=user_data.get("sessionId") if user_data else None, # STORAGE_VER 1
session=async_get_clientsession(hass),
)
@ -425,7 +425,7 @@ class EvoBroker:
self.tcs_utc_offset = timedelta(
minutes=client.locations[loc_idx].timeZone[UTC_OFFSET]
)
self.temps: dict[str, Any] | None = {}
self.temps: dict[str, float | None] = {}
async def save_auth_tokens(self) -> None:
"""Save access tokens and session IDs to the store for later use."""
@ -441,14 +441,12 @@ class EvoBroker:
ACCESS_TOKEN_EXPIRES: access_token_expires.isoformat(),
}
if self.client_v1 and self.client_v1.user_data:
user_id = self.client_v1.user_data["userInfo"]["userID"] # type: ignore[index]
if self.client_v1:
app_storage[USER_DATA] = { # type: ignore[assignment]
"userInfo": {"userID": user_id},
"sessionId": self.client_v1.user_data["sessionId"],
}
"sessionId": self.client_v1.broker.session_id,
} # this is the schema for STORAGE_VER == 1
else:
app_storage[USER_DATA] = None
app_storage[USER_DATA] = {} # type: ignore[assignment]
await self._store.async_save(app_storage)
@ -468,16 +466,13 @@ class EvoBroker:
async def _update_v1_api_temps(self, *args, **kwargs) -> None:
"""Get the latest high-precision temperatures of the default Location."""
assert self.client_v1
assert self.client_v1 # mypy check
def get_session_id(client_v1) -> str | None:
user_data = client_v1.user_data if client_v1 else None
return user_data.get("sessionId") if user_data else None
session_id = get_session_id(self.client_v1)
session_id = self.client_v1.broker.session_id # maybe receive a new session_id?
self.temps = {} # these are now stale, will fall back to v2 temps
try:
temps = list(await self.client_v1.temperatures(force_refresh=True))
temps = await self.client_v1.get_temperatures()
except evohomeasync.InvalidSchema as exc:
_LOGGER.warning(
@ -489,7 +484,7 @@ class EvoBroker:
),
exc,
)
self.temps = self.client_v1 = None
self.client_v1 = None
except evohomeasync.EvohomeError as exc:
_LOGGER.warning(
@ -501,7 +496,6 @@ class EvoBroker:
),
exc,
)
self.temps = None # these are now stale, will fall back to v2 temps
else:
if (
@ -513,19 +507,20 @@ class EvoBroker:
"the v1 API's default location (there is more than one location), "
"so the high-precision feature will be disabled until next restart"
)
self.temps = self.client_v1 = None
self.client_v1 = None
else:
self.temps = {str(i["id"]): i["temp"] for i in temps}
finally:
if session_id != get_session_id(self.client_v1):
if self.client_v1 and session_id != self.client_v1.broker.session_id:
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."""
access_token = self.client.access_token
access_token = self.client.access_token # maybe receive a new token?
loc_idx = self.params[CONF_LOCATION_IDX]
try:
@ -536,9 +531,9 @@ class EvoBroker:
async_dispatcher_send(self.hass, DOMAIN)
_LOGGER.debug("Status = %s", status)
if access_token != self.client.access_token:
await self.save_auth_tokens()
finally:
if access_token != self.client.access_token:
await self.save_auth_tokens()
async def async_update(self, *args, **kwargs) -> None:
"""Get the latest state data of an entire Honeywell TCC Location.
@ -562,6 +557,8 @@ class EvoDevice(Entity):
_attr_should_poll = False
_evo_id: str
def __init__(self, evo_broker, evo_device) -> None:
"""Initialize the evohome entity."""
self._evo_device = evo_device
@ -623,18 +620,10 @@ class EvoChild(EvoDevice):
@property
def current_temperature(self) -> float | None:
"""Return the current temperature of a Zone."""
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"]
return None
if self._evo_broker.temps.get(self._evo_id) is not None:
return self._evo_broker.temps[self._evo_id]
return self._evo_device.temperature
@property
def setpoints(self) -> dict[str, Any]:
@ -679,7 +668,7 @@ class EvoChild(EvoDevice):
switchpoint_time_of_day = dt_util.parse_datetime(
f"{sp_date}T{switchpoint['TimeOfDay']}"
)
assert switchpoint_time_of_day
assert switchpoint_time_of_day # mypy check
dt_aware = _dt_evo_to_aware(
switchpoint_time_of_day, self._evo_broker.tcs_utc_offset
)

View File

@ -150,6 +150,7 @@ class EvoZone(EvoChild, EvoClimateEntity):
self._attr_unique_id = f"{evo_device.zoneId}z"
else:
self._attr_unique_id = evo_device.zoneId
self._evo_id = evo_device.zoneId
self._attr_name = evo_device.name
@ -189,24 +190,27 @@ class EvoZone(EvoChild, EvoClimateEntity):
)
@property
def hvac_mode(self) -> HVACMode:
def hvac_mode(self) -> HVACMode | None:
"""Return the current operating mode of a Zone."""
if self._evo_tcs.systemModeStatus["mode"] in (EVO_AWAY, EVO_HEATOFF):
if self._evo_tcs.system_mode in (EVO_AWAY, EVO_HEATOFF):
return HVACMode.AUTO
is_off = self.target_temperature <= self.min_temp
return HVACMode.OFF if is_off else HVACMode.HEAT
if self.target_temperature is None:
return None
if self.target_temperature <= self.min_temp:
return HVACMode.OFF
return HVACMode.HEAT
@property
def target_temperature(self) -> float:
def target_temperature(self) -> float | None:
"""Return the target temperature of a Zone."""
return self._evo_device.setpointStatus["targetHeatTemperature"]
return self._evo_device.target_heat_temperature
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., home, away, temp."""
if self._evo_tcs.systemModeStatus["mode"] in (EVO_AWAY, EVO_HEATOFF):
return TCS_PRESET_TO_HA.get(self._evo_tcs.systemModeStatus["mode"])
return EVO_PRESET_TO_HA.get(self._evo_device.setpointStatus["setpointMode"])
if self._evo_tcs.system_mode in (EVO_AWAY, EVO_HEATOFF):
return TCS_PRESET_TO_HA.get(self._evo_tcs.system_mode)
return EVO_PRESET_TO_HA.get(self._evo_device.mode)
@property
def min_temp(self) -> float:
@ -214,7 +218,7 @@ class EvoZone(EvoChild, EvoClimateEntity):
The default is 5, but is user-configurable within 5-35 (in Celsius).
"""
return self._evo_device.setpointCapabilities["minHeatSetpoint"]
return self._evo_device.min_heat_setpoint
@property
def max_temp(self) -> float:
@ -222,17 +226,17 @@ class EvoZone(EvoChild, EvoClimateEntity):
The default is 35, but is user-configurable within 5-35 (in Celsius).
"""
return self._evo_device.setpointCapabilities["maxHeatSetpoint"]
return self._evo_device.max_heat_setpoint
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set a new target temperature."""
temperature = kwargs["temperature"]
if (until := kwargs.get("until")) is None:
if self._evo_device.setpointStatus["setpointMode"] == EVO_FOLLOW:
if self._evo_device.mode == EVO_FOLLOW:
await self._update_schedule()
until = dt_util.parse_datetime(self.setpoints.get("next_sp_from", ""))
elif self._evo_device.setpointStatus["setpointMode"] == EVO_TEMPOVER:
elif self._evo_device.mode == EVO_TEMPOVER:
until = dt_util.parse_datetime(self._evo_device.setpointStatus["until"])
until = dt_util.as_utc(until) if until else None
@ -272,7 +276,7 @@ class EvoZone(EvoChild, EvoClimateEntity):
await self._evo_broker.call_client_api(self._evo_device.reset_mode())
return
temperature = self._evo_device.setpointStatus["targetHeatTemperature"]
temperature = self._evo_device.target_heat_temperature
if evo_preset_mode == EVO_TEMPOVER:
await self._update_schedule()
@ -311,6 +315,7 @@ class EvoController(EvoClimateEntity):
super().__init__(evo_broker, evo_device)
self._attr_unique_id = evo_device.systemId
self._evo_id = evo_device.systemId
self._attr_name = evo_device.location.name
modes = [m["systemMode"] for m in evo_broker.config["allowedSystemModes"]]
@ -352,7 +357,7 @@ class EvoController(EvoClimateEntity):
@property
def hvac_mode(self) -> HVACMode:
"""Return the current operating mode of a Controller."""
tcs_mode = self._evo_tcs.systemModeStatus["mode"]
tcs_mode = self._evo_tcs.system_mode
return HVACMode.OFF if tcs_mode == EVO_HEATOFF else HVACMode.HEAT
@property
@ -362,16 +367,18 @@ class EvoController(EvoClimateEntity):
Controllers do not have a current temp, but one is expected by HA.
"""
temps = [
z.temperatureStatus["temperature"]
z.temperature
for z in self._evo_tcs.zones.values()
if z.temperatureStatus["isAvailable"]
if z.temperature is not None
]
return round(sum(temps) / len(temps), 1) if temps else None
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., home, away, temp."""
return TCS_PRESET_TO_HA.get(self._evo_tcs.systemModeStatus["mode"])
if not self._evo_tcs.system_mode:
return None
return TCS_PRESET_TO_HA.get(self._evo_tcs.system_mode)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Raise exception as Controllers don't have a target temperature."""

View File

@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/evohome",
"iot_class": "cloud_polling",
"loggers": ["evohomeasync", "evohomeasync2"],
"requirements": ["evohome-async==0.4.6"]
"requirements": ["evohome-async==0.4.9"]
}

View File

@ -68,6 +68,7 @@ class EvoDHW(EvoChild, WaterHeaterEntity):
super().__init__(evo_broker, evo_device)
self._attr_unique_id = evo_device.dhwId
self._evo_id = evo_device.dhwId
self._attr_precision = (
PRECISION_TENTHS if evo_broker.client_v1 else PRECISION_WHOLE
@ -79,15 +80,15 @@ class EvoDHW(EvoChild, WaterHeaterEntity):
@property
def current_operation(self) -> str:
"""Return the current operating mode (Auto, On, or Off)."""
if self._evo_device.stateStatus["mode"] == EVO_FOLLOW:
if self._evo_device.mode == EVO_FOLLOW:
return STATE_AUTO
return EVO_STATE_TO_HA[self._evo_device.stateStatus["state"]]
return EVO_STATE_TO_HA[self._evo_device.state]
@property
def is_away_mode_on(self):
"""Return True if away mode is on."""
is_off = EVO_STATE_TO_HA[self._evo_device.stateStatus["state"]] == STATE_OFF
is_permanent = self._evo_device.stateStatus["mode"] == EVO_PERMOVER
is_off = EVO_STATE_TO_HA[self._evo_device.state] == STATE_OFF
is_permanent = self._evo_device.mode == EVO_PERMOVER
return is_off and is_permanent
async def async_set_operation_mode(self, operation_mode: str) -> None:

View File

@ -789,7 +789,7 @@ eufylife-ble-client==0.1.8
# evdev==1.6.1
# homeassistant.components.evohome
evohome-async==0.4.6
evohome-async==0.4.9
# homeassistant.components.faa_delays
faadelays==2023.9.1