Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid concurrent radio operations with powerview hubs #114399

Merged
merged 4 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,5 @@ def __init__(

async def async_press(self) -> None:
"""Handle the button press."""
await self.entity_description.press_action(self._shade)
async with self.coordinator.radio_operation_lock:
await self.entity_description.press_action(self._shade)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import asyncio
from datetime import timedelta
import logging

Expand All @@ -25,6 +26,10 @@ def __init__(self, hass: HomeAssistant, shades: Shades, hub: Hub) -> None:
"""Initialize DataUpdateCoordinator to gather data for specific Powerview Hub."""
self.shades = shades
self.hub = hub
# The hub tends to crash if there are multiple radio operations at the same time
# but it seems to handle all other requests that do not use RF without issue
# so we have a lock to prevent multiple radio operations at the same time
self.radio_operation_lock = asyncio.Lock()
super().__init__(
hass,
_LOGGER,
Expand Down
11 changes: 8 additions & 3 deletions homeassistant/components/hunterdouglas_powerview/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ async def _async_initial_refresh() -> None:

for shade in pv_entry.shade_data.values():
_LOGGER.debug("Initial refresh of shade: %s", shade.name)
await shade.refresh(suppress_timeout=True) # default 15 second timeout
async with coordinator.radio_operation_lock:
await shade.refresh(suppress_timeout=True) # default 15 second timeout

entities: list[ShadeEntity] = []
for shade in pv_entry.shade_data.values():
Expand Down Expand Up @@ -207,7 +208,8 @@ def _get_shade_move(self, target_hass_position: int) -> ShadePosition:
async def _async_execute_move(self, move: ShadePosition) -> None:
"""Execute a move that can affect multiple positions."""
_LOGGER.debug("Move request %s: %s", self.name, move)
response = await self._shade.move(move)
async with self.coordinator.radio_operation_lock:
response = await self._shade.move(move)
_LOGGER.debug("Move response %s: %s", self.name, response)

# Process the response from the hub (including new positions)
Expand Down Expand Up @@ -318,7 +320,10 @@ async def async_update(self) -> None:
# error if are already have one in flight
return
# suppress timeouts caused by hub nightly reboot
await self._shade.refresh(suppress_timeout=True) # default 15 second timeout
async with self.coordinator.radio_operation_lock:
await self._shade.refresh(
suppress_timeout=True
) # default 15 second timeout
_LOGGER.debug("Process update %s: %s", self.name, self._shade.current_position)
self._async_update_shade_data(self._shade.current_position)

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/hunterdouglas_powerview/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class HDEntity(CoordinatorEntity[PowerviewShadeUpdateCoordinator]):
"""Base class for hunter douglas entities."""

_attr_has_entity_name = True
coordinator: PowerviewShadeUpdateCoordinator
bdraco marked this conversation as resolved.
Show resolved Hide resolved

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,6 @@ async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.entity_description.select_fn(self._shade, option)
# force update data to ensure new info is in coordinator
await self._shade.refresh()
async with self.coordinator.radio_operation_lock:
await self._shade.refresh()
bdraco marked this conversation as resolved.
Show resolved Hide resolved
self.async_write_ha_state()
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,6 @@ def _async_update_shade_from_group(self) -> None:

async def async_update(self) -> None:
"""Refresh sensor entity."""
await self.entity_description.update_fn(self._shade)
async with self.coordinator.radio_operation_lock:
await self.entity_description.update_fn(self._shade)
self.async_write_ha_state()
Loading