diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 1650edcec..16fb9de9d 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -48,13 +48,17 @@ """ import asyncio import logging +from datetime import timedelta, datetime from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.const import ( + CONF_DEVICE_ID, CONF_PLATFORM, CONF_ENTITIES, ) +from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.dispatcher import async_dispatcher_send from .const import DOMAIN, TUYA_DEVICE from .config_flow import config_schema @@ -63,6 +67,9 @@ _LOGGER = logging.getLogger(__name__) UNSUB_LISTENER = "unsub_listener" +UNSUB_TRACK = "unsub_track" + +POLL_INTERVAL = 30 CONFIG_SCHEMA = config_schema() @@ -85,17 +92,43 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) + device = TuyaDevice(entry.data) + + def update_state(now): + """Read device status and update platforms.""" + status = None + try: + status = device.status() + except Exception: + _LOGGER.exception("update failed") + + signal = f"localtuya_{entry.data[CONF_DEVICE_ID]}" + async_dispatcher_send(hass, signal, status) + + unsub_track = async_track_time_interval( + hass, update_state, timedelta(seconds=POLL_INTERVAL) + ) + hass.data[DOMAIN][entry.entry_id] = { UNSUB_LISTENER: unsub_listener, - TUYA_DEVICE: TuyaDevice(entry.data), + UNSUB_TRACK: unsub_track, + TUYA_DEVICE: device, } - for entity in entry.data[CONF_ENTITIES]: - platform = entity[CONF_PLATFORM] - hass.async_create_task( - hass.config_entries.async_forward_entry_setup(entry, platform) + async def setup_entities(): + await asyncio.gather( + *[ + hass.config_entries.async_forward_entry_setup( + entry, entity[CONF_PLATFORM] + ) + for entity in entry.data[CONF_ENTITIES] + ] ) + update_state(datetime.now()) + + hass.async_create_task(setup_entities()) + return True @@ -113,6 +146,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ) hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]() + hass.data[DOMAIN][entry.entry_id][UNSUB_TRACK]() if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) diff --git a/custom_components/localtuya/binary_sensor.py b/custom_components/localtuya/binary_sensor.py index 2d7ecb2f3..795e96886 100644 --- a/custom_components/localtuya/binary_sensor.py +++ b/custom_components/localtuya/binary_sensor.py @@ -45,7 +45,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(sensors, True) + async_add_entities(sensors) class LocaltuyaBinarySensor(LocalTuyaEntity, BinarySensorEntity): diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 91bb2267b..ea3bdd015 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -4,6 +4,7 @@ from threading import Lock from homeassistant.helpers.entity import Entity +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.const import ( CONF_DEVICE_ID, @@ -132,9 +133,27 @@ def __init__(self, device, config_entry, dps_id, **kwargs): self._device = device self._config_entry = config_entry self._config = get_entity_config(config_entry, dps_id) - self._available = False self._dps_id = dps_id - self._status = None + self._status = {} + + async def async_added_to_hass(self): + """Subscribe localtuya events.""" + await super().async_added_to_hass() + + def _update_handler(status): + """Update entity state when status was updated.""" + if status is not None: + self._status = status + self.status_updated() + else: + self._status = {} + + self.schedule_update_ha_state() + + signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}" + self.async_on_remove( + async_dispatcher_connect(self.hass, signal, _update_handler) + ) @property def device_info(self): @@ -155,6 +174,11 @@ def name(self): """Get name of Tuya entity.""" return self._config[CONF_FRIENDLY_NAME] + @property + def should_poll(self): + """Return if platform should poll for updates.""" + return False + @property def unique_id(self): """Return unique device identifier.""" @@ -163,10 +187,13 @@ def unique_id(self): @property def available(self): """Return if device is available or not.""" - return self._available + return bool(self._status) def dps(self, dps_index): """Return cached value for DPS index.""" + if "dps" not in self._status: + return None + value = self._status["dps"].get(str(dps_index)) if value is None: _LOGGER.warning( @@ -174,17 +201,8 @@ def dps(self, dps_index): self.entity_id, dps_index, ) - return value - def update(self): - """Update state of Tuya entity.""" - try: - self._status = self._device.status() - self.status_updated() - except Exception: - self._available = False - else: - self._available = True + return value def status_updated(self): """Device status was updated. diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index ec2d1e9ca..9335408f7 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -55,7 +55,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(covers, True) + async_add_entities(covers) class LocaltuyaCover(LocalTuyaEntity, CoverEntity): @@ -78,11 +78,6 @@ def __init__( ) ) - @property - def available(self): - """Return if device is available or not.""" - return self._available - @property def supported_features(self): """Flag supported features.""" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 10776720b..8d14a0b93 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -42,7 +42,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(fans, True) + async_add_entities(fans) class LocaltuyaFan(LocalTuyaEntity, FanEntity): diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index 0f3ca7c79..2dac1c74b 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -50,7 +50,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(lights, True) + async_add_entities(lights) class LocaltuyaLight(LocalTuyaEntity, LightEntity): diff --git a/custom_components/localtuya/sensor.py b/custom_components/localtuya/sensor.py index 870e48aa7..659e6a9bb 100644 --- a/custom_components/localtuya/sensor.py +++ b/custom_components/localtuya/sensor.py @@ -49,7 +49,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(sensors, True) + async_add_entities(sensors) class LocaltuyaSensor(LocalTuyaEntity): diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index 0ae6075fe..e9cb01d81 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -49,7 +49,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) ) - async_add_entities(switches, True) + async_add_entities(switches) class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):