Switch to update coordinator, and bump venstarcolortouch to 0.15 (#58601)

This commit is contained in:
Tim Rightnour 2021-10-30 15:23:47 -07:00 committed by GitHub
parent 6c426fea9e
commit f87f72bb8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 43 deletions

View File

@ -1,9 +1,11 @@
"""The venstar component.""" """The venstar component."""
import asyncio import asyncio
from datetime import timedelta
from requests import RequestException from requests import RequestException
from venstarcolortouch import VenstarColorTouch from venstarcolortouch import VenstarColorTouch
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_PASSWORD, CONF_PASSWORD,
@ -11,8 +13,9 @@ from homeassistant.const import (
CONF_SSL, CONF_SSL,
CONF_USERNAME, CONF_USERNAME,
) )
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import Entity from homeassistant.helpers import update_coordinator
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import _LOGGER, DOMAIN, VENSTAR_TIMEOUT from .const import _LOGGER, DOMAIN, VENSTAR_TIMEOUT
@ -37,11 +40,13 @@ async def async_setup_entry(hass, config):
proto=protocol, proto=protocol,
) )
try: venstar_data_coordinator = VenstarDataUpdateCoordinator(
await hass.async_add_executor_job(client.update_info) hass,
except (OSError, RequestException) as ex: venstar_connection=client,
raise ConfigEntryNotReady(f"Unable to connect to the thermostat: {ex}") from ex )
hass.data.setdefault(DOMAIN, {})[config.entry_id] = client await venstar_data_coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[config.entry_id] = venstar_data_coordinator
hass.config_entries.async_setup_platforms(config, PLATFORMS) hass.config_entries.async_setup_platforms(config, PLATFORMS)
return True return True
@ -55,35 +60,74 @@ async def async_unload_entry(hass, config):
return unload_ok return unload_ok
class VenstarEntity(Entity): class VenstarDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator):
"""Get the latest data and update.""" """Class to manage fetching Venstar data."""
def __init__(self, config, client): def __init__(
"""Initialize the data object.""" self,
self._config = config hass: HomeAssistant,
self._client = client *,
venstar_connection: VenstarColorTouch,
) -> None:
"""Initialize global Venstar data updater."""
self.client = venstar_connection
async def async_update(self): super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=60),
)
async def _async_update_data(self) -> None:
"""Update the state.""" """Update the state."""
try: try:
info_success = await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(self.client.update_info)
self._client.update_info
)
except (OSError, RequestException) as ex: except (OSError, RequestException) as ex:
_LOGGER.error("Exception during info update: %s", ex) raise update_coordinator.UpdateFailed(
f"Exception during Venstar info update: {ex}"
) from ex
# older venstars sometimes cannot handle rapid sequential connections # older venstars sometimes cannot handle rapid sequential connections
await asyncio.sleep(3) await asyncio.sleep(3)
try: try:
sensor_success = await self.hass.async_add_executor_job( await self.hass.async_add_executor_job(self.client.update_sensors)
self._client.update_sensors
)
except (OSError, RequestException) as ex: except (OSError, RequestException) as ex:
_LOGGER.error("Exception during sensor update: %s", ex) raise update_coordinator.UpdateFailed(
f"Exception during Venstar sensor update: {ex}"
) from ex
return None
if not info_success or not sensor_success:
_LOGGER.error("Failed to update data") class VenstarEntity(CoordinatorEntity):
"""Representation of a Venstar entity."""
def __init__(
self,
venstar_data_coordinator: VenstarDataUpdateCoordinator,
config: ConfigEntry,
) -> None:
"""Initialize the data object."""
super().__init__(venstar_data_coordinator)
self._config = config
self._update_attr()
self.coordinator = venstar_data_coordinator
@property
def _client(self):
"""Return the venstar client."""
return self.coordinator.client
@callback
def _update_attr(self) -> None:
"""Update the state and attributes."""
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_attr()
self.async_write_ha_state()
@property @property
def name(self): def name(self):
@ -102,8 +146,6 @@ class VenstarEntity(Entity):
"identifiers": {(DOMAIN, self._config.entry_id)}, "identifiers": {(DOMAIN, self._config.entry_id)},
"name": self._client.name, "name": self._client.name,
"manufacturer": "Venstar", "manufacturer": "Venstar",
# pylint: disable=protected-access "model": f"{self._client.model}-{self._client.get_type()}",
"model": f"{self._client.model}-{self._client._type}", "sw_version": self._client.get_api_ver(),
# pylint: disable=protected-access
"sw_version": self._client._api_ver,
} }

View File

@ -1,4 +1,6 @@
"""Support for Venstar WiFi Thermostats.""" """Support for Venstar WiFi Thermostats."""
from functools import partial
import voluptuous as vol import voluptuous as vol
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
@ -24,7 +26,7 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_RANGE, SUPPORT_TARGET_TEMPERATURE_RANGE,
) )
from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
CONF_HOST, CONF_HOST,
@ -38,9 +40,11 @@ from homeassistant.const import (
TEMP_CELSIUS, TEMP_CELSIUS,
TEMP_FAHRENHEIT, TEMP_FAHRENHEIT,
) )
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import VenstarEntity from . import VenstarDataUpdateCoordinator, VenstarEntity
from .const import ( from .const import (
_LOGGER, _LOGGER,
ATTR_FAN_STATE, ATTR_FAN_STATE,
@ -68,10 +72,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
) )
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Venstar thermostat.""" """Set up the Venstar thermostat."""
client = hass.data[DOMAIN][config_entry.entry_id] venstar_data_coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities([VenstarThermostat(config_entry, client)], True) async_add_entities(
[
VenstarThermostat(
venstar_data_coordinator,
config_entry,
)
],
)
async def async_setup_platform(hass, config, add_entities, discovery_info=None): async def async_setup_platform(hass, config, add_entities, discovery_info=None):
@ -95,9 +110,13 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
class VenstarThermostat(VenstarEntity, ClimateEntity): class VenstarThermostat(VenstarEntity, ClimateEntity):
"""Representation of a Venstar thermostat.""" """Representation of a Venstar thermostat."""
def __init__(self, config, client): def __init__(
self,
venstar_data_coordinator: VenstarDataUpdateCoordinator,
config: ConfigEntry,
) -> None:
"""Initialize the thermostat.""" """Initialize the thermostat."""
super().__init__(config, client) super().__init__(venstar_data_coordinator, config)
self._mode_map = { self._mode_map = {
HVAC_MODE_HEAT: self._client.MODE_HEAT, HVAC_MODE_HEAT: self._client.MODE_HEAT,
HVAC_MODE_COOL: self._client.MODE_COOL, HVAC_MODE_COOL: self._client.MODE_COOL,
@ -257,7 +276,12 @@ class VenstarThermostat(VenstarEntity, ClimateEntity):
_LOGGER.error("Failed to change the operation mode") _LOGGER.error("Failed to change the operation mode")
return success return success
def set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs):
"""Set a new target temperature."""
await self.hass.async_add_executor_job(partial(self._set_temperature, **kwargs))
self.async_write_ha_state()
def _set_temperature(self, **kwargs):
"""Set a new target temperature.""" """Set a new target temperature."""
set_temp = True set_temp = True
operation_mode = kwargs.get(ATTR_HVAC_MODE) operation_mode = kwargs.get(ATTR_HVAC_MODE)
@ -295,7 +319,12 @@ class VenstarThermostat(VenstarEntity, ClimateEntity):
if not success: if not success:
_LOGGER.error("Failed to change the temperature") _LOGGER.error("Failed to change the temperature")
def set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set a new target fan mode."""
await self.hass.async_add_executor_job(self._set_fan_mode, fan_mode)
self.async_write_ha_state()
def _set_fan_mode(self, fan_mode):
"""Set new target fan mode.""" """Set new target fan mode."""
if fan_mode == STATE_ON: if fan_mode == STATE_ON:
success = self._client.set_fan(self._client.FAN_ON) success = self._client.set_fan(self._client.FAN_ON)
@ -305,18 +334,33 @@ class VenstarThermostat(VenstarEntity, ClimateEntity):
if not success: if not success:
_LOGGER.error("Failed to change the fan mode") _LOGGER.error("Failed to change the fan mode")
def set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set a new target operation mode."""
await self.hass.async_add_executor_job(self._set_hvac_mode, hvac_mode)
self.async_write_ha_state()
def _set_hvac_mode(self, hvac_mode):
"""Set new target operation mode.""" """Set new target operation mode."""
self._set_operation_mode(hvac_mode) self._set_operation_mode(hvac_mode)
def set_humidity(self, humidity): async def async_set_humidity(self, humidity: int) -> None:
"""Set a new target humidity."""
await self.hass.async_add_executor_job(self._set_humidity, humidity)
self.async_write_ha_state()
def _set_humidity(self, humidity):
"""Set new target humidity.""" """Set new target humidity."""
success = self._client.set_hum_setpoint(humidity) success = self._client.set_hum_setpoint(humidity)
if not success: if not success:
_LOGGER.error("Failed to change the target humidity level") _LOGGER.error("Failed to change the target humidity level")
def set_preset_mode(self, preset_mode): async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the hold mode."""
await self.hass.async_add_executor_job(self._set_preset_mode, preset_mode)
self.async_write_ha_state()
def _set_preset_mode(self, preset_mode):
"""Set the hold mode.""" """Set the hold mode."""
if preset_mode == PRESET_AWAY: if preset_mode == PRESET_AWAY:
success = self._client.set_away(self._client.AWAY_AWAY) success = self._client.set_away(self._client.AWAY_AWAY)

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/venstar", "documentation": "https://www.home-assistant.io/integrations/venstar",
"requirements": [ "requirements": [
"venstarcolortouch==0.14" "venstarcolortouch==0.15"
], ],
"codeowners": ["@garbled1"], "codeowners": ["@garbled1"],
"iot_class": "local_polling" "iot_class": "local_polling"

View File

@ -2369,7 +2369,7 @@ vallox-websocket-api==2.8.1
velbus-aio==2021.10.7 velbus-aio==2021.10.7
# homeassistant.components.venstar # homeassistant.components.venstar
venstarcolortouch==0.14 venstarcolortouch==0.15
# homeassistant.components.vilfo # homeassistant.components.vilfo
vilfo-api-client==0.3.2 vilfo-api-client==0.3.2

View File

@ -1373,7 +1373,7 @@ uvcclient==0.11.0
velbus-aio==2021.10.7 velbus-aio==2021.10.7
# homeassistant.components.venstar # homeassistant.components.venstar
venstarcolortouch==0.14 venstarcolortouch==0.15
# homeassistant.components.vilfo # homeassistant.components.vilfo
vilfo-api-client==0.3.2 vilfo-api-client==0.3.2