diff --git a/custom_components/hilo/__init__.py b/custom_components/hilo/__init__.py index cf807f5..c269f90 100644 --- a/custom_components/hilo/__init__.py +++ b/custom_components/hilo/__init__.py @@ -243,7 +243,7 @@ def validate_heartbeat(self, event: WebsocketEvent) -> None: LOG.debug(f"Heartbeat: {time_diff(heartbeat_time, event.timestamp)}") @callback - def on_websocket_event(self, event: WebsocketEvent) -> None: + async def on_websocket_event(self, event: WebsocketEvent) -> None: """Define a callback for receiving a websocket event.""" async_dispatcher_send(self._hass, DISPATCHER_TOPIC_WEBSOCKET_EVENT, event) if event.event_type == "COMPLETE": @@ -253,6 +253,12 @@ def on_websocket_event(self, event: WebsocketEvent) -> None: elif event.target == "Heartbeat": self.validate_heartbeat(event) elif event.target == "DevicesValuesReceived": + # When receiving attribute values for unknown devices, assume + # we have refresh the device list. + new_devices = any(self.devices.find_device(item['deviceId']) is None for item in event.arguments[0]) + if new_devices: + await self.devices.update() + updated_devices = self.devices.parse_values_received(event.arguments[0]) # NOTE(dvd): If we don't do this, we need to wait until the coordinator # runs (scan_interval) to have updated data in the dashboard. @@ -260,6 +266,15 @@ def on_websocket_event(self, event: WebsocketEvent) -> None: async_dispatcher_send( self._hass, SIGNAL_UPDATE_ENTITY.format(device.id) ) + elif event.target == "DevicesListChanged": + # DeviceListChanged only triggers when unpairing devices + # Forcing an update when that happens, even tho pyhilo doesn't + # manage device removal currently. + await self.devices.update() + elif event.target == "GatewayValuesReceived": + # Placeholder for new event that will allow Gateway updates without + # calling update_gateway explicitly in async_update + LOG.debug("GatewayValuesReceived") else: LOG.warning(f"Unhandled websocket event: {event}") @@ -405,8 +420,8 @@ async def cancel_websocket_loop(self) -> None: await self._api.websocket.async_disconnect() async def async_update(self) -> None: - """Get updated data from Hilo API.""" - await self.devices.update() + """Updates gateway and tarif periodically.""" + await self.devices.update_gateway() if self.generate_energy_meters: self.check_tarif() diff --git a/custom_components/hilo/const.py b/custom_components/hilo/const.py index 1d0a886..348d197 100644 --- a/custom_components/hilo/const.py +++ b/custom_components/hilo/const.py @@ -32,8 +32,8 @@ CONF_UNTARIFICATED_DEVICES = "untarificated_devices" DEFAULT_UNTARIFICATED_DEVICES = False -DEFAULT_SCAN_INTERVAL = 60 -EVENT_SCAN_INTERVAL = 600 +DEFAULT_SCAN_INTERVAL = 300 +EVENT_SCAN_INTERVAL = 3000 REWARD_SCAN_INTERVAL = 7200 MIN_SCAN_INTERVAL = 15 diff --git a/custom_components/hilo/light.py b/custom_components/hilo/light.py index 94626da..74335cc 100644 --- a/custom_components/hilo/light.py +++ b/custom_components/hilo/light.py @@ -6,13 +6,13 @@ ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import slugify from . import Hilo, HiloEntity from .const import DOMAIN, LIGHT_CLASSES, LOG - async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: @@ -21,15 +21,22 @@ async def async_setup_entry( for d in hilo.devices.all: if d.type in LIGHT_CLASSES: - d._entity = HiloLight(hilo, d) + d._entity = HiloLight(hass, hilo, d) entities.append(d._entity) async_add_entities(entities) class HiloLight(HiloEntity, LightEntity): - def __init__(self, hilo: Hilo, device): + def __init__(self, hass: HomeAssistant, hilo: Hilo, device): super().__init__(hilo, device=device, name=device.name) self._attr_unique_id = f"{slugify(device.name)}-light" + self._debounced_turn_on = Debouncer( + hass, + LOG, + cooldown=1, + immediate=True, + function=self._async_debounced_turn_on + ) LOG.debug(f"Setting up Light entity: {self._attr_name}") @property @@ -74,15 +81,19 @@ async def async_turn_off(self, **kwargs): self.async_schedule_update_ha_state(True) async def async_turn_on(self, **kwargs): + self._last_kwargs = kwargs + await self._debounced_turn_on.async_call() + + async def _async_debounced_turn_on(self): LOG.info(f"{self._device._tag} Turning on") await self._device.set_attribute("is_on", True) - if ATTR_BRIGHTNESS in kwargs: + if ATTR_BRIGHTNESS in self._last_kwargs: LOG.info( - f"{self._device._tag} Setting brightness to {kwargs[ATTR_BRIGHTNESS]}" + f"{self._device._tag} Setting brightness to {self._last_kwargs[ATTR_BRIGHTNESS]}" ) - await self._device.set_attribute("intensity", kwargs[ATTR_BRIGHTNESS] / 255) - if ATTR_HS_COLOR in kwargs: - LOG.info(f"{self._device._tag} Setting HS Color to {kwargs[ATTR_HS_COLOR]}") - await self._device.set_attribute("hue", kwargs[ATTR_HS_COLOR][0]) - await self._device.set_attribute("saturation", kwargs[ATTR_HS_COLOR][1]) + await self._device.set_attribute("intensity", self._last_kwargs[ATTR_BRIGHTNESS] / 255) + if ATTR_HS_COLOR in self._last_kwargs: + LOG.info(f"{self._device._tag} Setting HS Color to {self._last_kwargs[ATTR_HS_COLOR]}") + await self._device.set_attribute("hue", self._last_kwargs[ATTR_HS_COLOR][0]) + await self._device.set_attribute("saturation", self._last_kwargs[ATTR_HS_COLOR][1]) self.async_schedule_update_ha_state(True) diff --git a/custom_components/hilo/manifest.json b/custom_components/hilo/manifest.json index 16ebaf2..1207cf7 100644 --- a/custom_components/hilo/manifest.json +++ b/custom_components/hilo/manifest.json @@ -9,7 +9,7 @@ "codeowners": ["@dvd-dev"], "config_flow": true, "documentation": "https://github.com/dvd-dev/hilo", - "iot_class": "cloud_polling", + "iot_class": "cloud_push", "issue_tracker": "https://github.com/dvd-dev/hilo/issues", "requirements": ["python-hilo>=2023.3.1"], "version": "2023.3.2"