1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-31 20:54:23 +01:00
Files
core/homeassistant/components/group/util.py
T
2026-05-14 16:29:56 -04:00

102 lines
2.8 KiB
Python

"""Utility functions to combine state attributes from multiple entities."""
from collections.abc import Callable, Iterator
from itertools import groupby
from math import atan2, cos, degrees, radians, sin
from typing import Any
from homeassistant.core import State
def find_state_attributes(states: list[State], key: str) -> Iterator[Any]:
"""Find attributes with matching key from states."""
for state in states:
if (value := state.attributes.get(key)) is not None:
yield value
def find_state(states: list[State]) -> Iterator[Any]:
"""Find state from states."""
for state in states:
yield state.state
def mean_int(*args: Any) -> int:
"""Return the mean of the supplied values."""
return int(sum(args) / len(args))
def mean_tuple(*args: Any) -> tuple[float | Any, ...]:
"""Return the mean values along the columns of the supplied values."""
return tuple(sum(x) / len(x) for x in zip(*args, strict=False))
def mean_circle(*args: Any) -> tuple[float | Any, ...]:
"""Return circular mean of hue and arithmetic mean of saturation from HS tuples."""
if not args:
return ()
hues, saturations = zip(*args, strict=False)
sum_x = sum(cos(radians(h)) for h in hues)
sum_y = sum(sin(radians(h)) for h in hues)
mean_angle = degrees(atan2(sum_y, sum_x)) % 360
saturation = sum(saturations) / len(saturations)
return (mean_angle, saturation)
def attribute_equal(states: list[State], key: str) -> bool:
"""Return True if all attributes found matching key from states are equal.
Note: Returns True if no matching attribute is found.
"""
return _values_equal(find_state_attributes(states, key))
def most_frequent_attribute(states: list[State], key: str) -> Any | None:
"""Find attributes with matching key from states."""
if attrs := list(find_state_attributes(states, key)):
return max(set(attrs), key=attrs.count)
return None
def states_equal(states: list[State]) -> bool:
"""Return True if all states are equal.
Note: Returns True if no matching attribute is found.
"""
return _values_equal(find_state(states))
def _values_equal(values: Iterator[Any]) -> bool:
"""Return True if all values are equal.
Note: Returns True if no matching attribute is found.
"""
grp = groupby(values)
return bool(next(grp, True) and not next(grp, False))
def reduce_attribute(
states: list[State],
key: str,
default: Any | None = None,
reduce: Callable[..., Any] = mean_int,
) -> Any:
"""Find the first attribute matching key from states.
If none are found, return default.
"""
attrs = list(find_state_attributes(states, key))
if not attrs:
return default
if len(attrs) == 1:
return attrs[0]
return reduce(*attrs)