Skip to content

Commit

Permalink
Fix missing powerview shade data when initial refresh fails (home-ass…
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Mar 27, 2024
1 parent 5316b94 commit ce022a1
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 39 deletions.
1 change: 0 additions & 1 deletion homeassistant/components/hunterdouglas_powerview/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ async def _async_initial_refresh() -> None:

entities: list[ShadeEntity] = []
for shade in pv_entry.shade_data.values():
coordinator.data.update_shade_position(shade.id, shade.current_position)
room_name = getattr(pv_entry.room_data.get(shade.room_id), ATTR_NAME, "")
entities.extend(
create_powerview_shade_entity(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/hunterdouglas_powerview/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def store_velocity(
value: float | None,
) -> None:
"""Store the desired shade velocity in the coordinator."""
coordinator.data.update_shade_velocity(shade_id, ShadePosition(velocity=value))
coordinator.data.update_shade_position(shade_id, ShadePosition(velocity=value))


NUMBERS: Final = (
Expand Down
67 changes: 30 additions & 37 deletions homeassistant/components/hunterdouglas_powerview/shade_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from dataclasses import fields
import logging
from typing import Any

Expand All @@ -12,74 +13,66 @@

_LOGGER = logging.getLogger(__name__)

POSITION_FIELDS = fields(ShadePosition)


def copy_position_data(source: ShadePosition, target: ShadePosition) -> ShadePosition:
"""Copy position data from source to target for None values only."""
# the hub will always return a velocity of 0 on initial connect,
# separate definition to store consistent value in HA
# this value is purely driven from HA
for field in POSITION_FIELDS:
if (value := getattr(source, field.name)) is not None:
setattr(target, field.name, value)


class PowerviewShadeData:
"""Coordinate shade data between multiple api calls."""

def __init__(self) -> None:
"""Init the shade data."""
self._group_data_by_id: dict[int, dict[str | int, Any]] = {}
self._shade_data_by_id: dict[int, BaseShade] = {}
self._raw_data_by_id: dict[int, dict[str | int, Any]] = {}
self._shade_group_data_by_id: dict[int, BaseShade] = {}
self.positions: dict[int, ShadePosition] = {}

def get_raw_data(self, shade_id: int) -> dict[str | int, Any]:
"""Get data for the shade."""
return self._group_data_by_id[shade_id]
return self._raw_data_by_id[shade_id]

def get_all_raw_data(self) -> dict[int, dict[str | int, Any]]:
"""Get data for all shades."""
return self._group_data_by_id
return self._raw_data_by_id

def get_shade(self, shade_id: int) -> BaseShade:
"""Get specific shade from the coordinator."""
return self._shade_data_by_id[shade_id]
return self._shade_group_data_by_id[shade_id]

def get_shade_position(self, shade_id: int) -> ShadePosition:
"""Get positions for a shade."""
if shade_id not in self.positions:
self.positions[shade_id] = ShadePosition()
shade_position = ShadePosition()
# If we have the group data, use it to populate the initial position
if shade := self._shade_group_data_by_id.get(shade_id):
copy_position_data(shade.current_position, shade_position)
self.positions[shade_id] = shade_position
return self.positions[shade_id]

def update_from_group_data(self, shade_id: int) -> None:
"""Process an update from the group data."""
self.update_shade_positions(self._shade_data_by_id[shade_id])
data = self._shade_group_data_by_id[shade_id]
copy_position_data(data.current_position, self.get_shade_position(data.id))

def store_group_data(self, shade_data: PowerviewData) -> None:
"""Store data from the all shades endpoint.
This does not update the shades or positions
This does not update the shades or positions (self.positions)
as the data may be stale. update_from_group_data
with a shade_id will update a specific shade
from the group data.
"""
self._shade_data_by_id = shade_data.processed
self._group_data_by_id = async_map_data_by_id(shade_data.raw)
self._shade_group_data_by_id = shade_data.processed
self._raw_data_by_id = async_map_data_by_id(shade_data.raw)

def update_shade_position(self, shade_id: int, shade_data: ShadePosition) -> None:
def update_shade_position(self, shade_id: int, new_position: ShadePosition) -> None:
"""Update a single shades position."""
if shade_id not in self.positions:
self.positions[shade_id] = ShadePosition()

# ShadePosition will return None if the value is not set
if shade_data.primary is not None:
self.positions[shade_id].primary = shade_data.primary
if shade_data.secondary is not None:
self.positions[shade_id].secondary = shade_data.secondary
if shade_data.tilt is not None:
self.positions[shade_id].tilt = shade_data.tilt

def update_shade_velocity(self, shade_id: int, shade_data: ShadePosition) -> None:
"""Update a single shades velocity."""
if shade_id not in self.positions:
self.positions[shade_id] = ShadePosition()

# the hub will always return a velocity of 0 on initial connect,
# separate definition to store consistent value in HA
# this value is purely driven from HA
if shade_data.velocity is not None:
self.positions[shade_id].velocity = shade_data.velocity

def update_shade_positions(self, data: BaseShade) -> None:
"""Update a shades from data dict."""
_LOGGER.debug("Raw data update: %s", data.raw_data)
self.update_shade_position(data.id, data.current_position)
copy_position_data(new_position, self.get_shade_position(shade_id))

0 comments on commit ce022a1

Please sign in to comment.