From 120479acef9a8e9e52fa356f036e55465e441d31 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Jun 2022 04:10:01 -0500 Subject: [PATCH] Enable polling for hardwired powerview devices (#73659) * Enable polling for hardwired powerview devices * Update homeassistant/components/hunterdouglas_powerview/cover.py * Update homeassistant/components/hunterdouglas_powerview/cover.py * docs were wrong * Update homeassistant/components/hunterdouglas_powerview/cover.py * Update homeassistant/components/hunterdouglas_powerview/sensor.py --- .../hunterdouglas_powerview/const.py | 6 +++ .../hunterdouglas_powerview/cover.py | 41 +++++++++++++++++-- .../hunterdouglas_powerview/entity.py | 10 ++--- .../hunterdouglas_powerview/sensor.py | 4 ++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/hunterdouglas_powerview/const.py b/homeassistant/components/hunterdouglas_powerview/const.py index 7146d40c737..65c461b6f2f 100644 --- a/homeassistant/components/hunterdouglas_powerview/const.py +++ b/homeassistant/components/hunterdouglas_powerview/const.py @@ -87,3 +87,9 @@ POS_KIND_PRIMARY = 1 POS_KIND_SECONDARY = 2 POS_KIND_VANE = 3 POS_KIND_ERROR = 4 + + +ATTR_BATTERY_KIND = "batteryKind" +BATTERY_KIND_HARDWIRED = 1 +BATTERY_KIND_BATTERY = 2 +BATTERY_KIND_RECHARGABLE = 3 diff --git a/homeassistant/components/hunterdouglas_powerview/cover.py b/homeassistant/components/hunterdouglas_powerview/cover.py index c3061c75301..e0a01f9c381 100644 --- a/homeassistant/components/hunterdouglas_powerview/cover.py +++ b/homeassistant/components/hunterdouglas_powerview/cover.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio from collections.abc import Iterable from contextlib import suppress +from datetime import timedelta import logging from typing import Any @@ -74,6 +75,8 @@ RESYNC_DELAY = 60 # implemented for top/down shades, but also works fine with normal shades CLOSED_POSITION = (0.75 / 100) * (MAX_POSITION - MIN_POSITION) +SCAN_INTERVAL = timedelta(minutes=10) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -152,8 +155,6 @@ def hass_position_to_hd(hass_position: int, max_val: int = MAX_POSITION) -> int: class PowerViewShadeBase(ShadeEntity, CoverEntity): """Representation of a powerview shade.""" - # The hub frequently reports stale states - _attr_assumed_state = True _attr_device_class = CoverDeviceClass.SHADE _attr_supported_features = 0 @@ -174,6 +175,26 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity): self._attr_supported_features |= CoverEntityFeature.STOP self._forced_resync = None + @property + def assumed_state(self) -> bool: + """If the device is hard wired we are polling state. + + The hub will frequently provide the wrong state + for battery power devices so we set assumed + state in this case. + """ + return not self._is_hard_wired + + @property + def should_poll(self) -> bool: + """Only poll if the device is hard wired. + + We cannot poll battery powered devices + as it would drain their batteries in a matter + of days. + """ + return self._is_hard_wired + @property def extra_state_attributes(self) -> dict[str, str]: """Return the state attributes.""" @@ -336,15 +357,29 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity): """Cancel any pending refreshes.""" self._async_cancel_scheduled_transition_update() + @property + def _update_in_progress(self) -> bool: + """Check if an update is already in progress.""" + return bool(self._scheduled_transition_update or self._forced_resync) + @callback def _async_update_shade_from_group(self) -> None: """Update with new data from the coordinator.""" - if self._scheduled_transition_update or self._forced_resync: + if self._update_in_progress: # If a transition is in progress the data will be wrong return self.data.update_from_group_data(self._shade.id) self.async_write_ha_state() + async def async_update(self) -> None: + """Refresh shade position.""" + if self._update_in_progress: + # The update will likely timeout and + # error if are already have one in flight + return + await self._shade.refresh() + self._async_update_shade_data(self._shade.raw_data) + class PowerViewShade(PowerViewShadeBase): """Represent a standard shade.""" diff --git a/homeassistant/components/hunterdouglas_powerview/entity.py b/homeassistant/components/hunterdouglas_powerview/entity.py index 7814ba9cb12..222324eb55a 100644 --- a/homeassistant/components/hunterdouglas_powerview/entity.py +++ b/homeassistant/components/hunterdouglas_powerview/entity.py @@ -10,6 +10,8 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ( + ATTR_BATTERY_KIND, + BATTERY_KIND_HARDWIRED, DEVICE_FIRMWARE, DEVICE_MAC_ADDRESS, DEVICE_MODEL, @@ -83,6 +85,9 @@ class ShadeEntity(HDEntity): super().__init__(coordinator, device_info, room_name, shade.id) self._shade_name = shade_name self._shade = shade + self._is_hard_wired = bool( + shade.raw_data.get(ATTR_BATTERY_KIND) == BATTERY_KIND_HARDWIRED + ) @property def positions(self) -> PowerviewShadePositions: @@ -117,8 +122,3 @@ class ShadeEntity(HDEntity): device_info[ATTR_SW_VERSION] = sw_version return device_info - - async def async_update(self) -> None: - """Refresh shade position.""" - await self._shade.refresh() - self.data.update_shade_positions(self._shade.raw_data) diff --git a/homeassistant/components/hunterdouglas_powerview/sensor.py b/homeassistant/components/hunterdouglas_powerview/sensor.py index 8fd492ddb1d..3fc8942eb78 100644 --- a/homeassistant/components/hunterdouglas_powerview/sensor.py +++ b/homeassistant/components/hunterdouglas_powerview/sensor.py @@ -91,3 +91,7 @@ class PowerViewShadeBatterySensor(ShadeEntity, SensorEntity): """Update with new data from the coordinator.""" self._shade.raw_data = self.data.get_raw_data(self._shade.id) self.async_write_ha_state() + + async def async_update(self) -> None: + """Refresh shade battery.""" + await self._shade.refreshBattery()