1 """Platform allowing several cover to be grouped into one cover."""
3 from __future__
import annotations
7 import voluptuous
as vol
10 ATTR_CURRENT_POSITION,
11 ATTR_CURRENT_TILT_POSITION,
14 DOMAIN
as COVER_DOMAIN,
15 PLATFORM_SCHEMA
as COVER_PLATFORM_SCHEMA,
23 ATTR_SUPPORTED_FEATURES,
28 SERVICE_CLOSE_COVER_TILT,
30 SERVICE_OPEN_COVER_TILT,
31 SERVICE_SET_COVER_POSITION,
32 SERVICE_SET_COVER_TILT_POSITION,
34 SERVICE_STOP_COVER_TILT,
43 from .entity
import GroupEntity
44 from .util
import reduce_attribute
46 KEY_OPEN_CLOSE =
"open_close"
48 KEY_POSITION =
"position"
50 DEFAULT_NAME =
"Cover Group"
55 PLATFORM_SCHEMA = COVER_PLATFORM_SCHEMA.extend(
57 vol.Required(CONF_ENTITIES): cv.entities_domain(COVER_DOMAIN),
58 vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
59 vol.Optional(CONF_UNIQUE_ID): cv.string,
67 async_add_entities: AddEntitiesCallback,
68 discovery_info: DiscoveryInfoType |
None =
None,
70 """Set up the Cover Group platform."""
74 config.get(CONF_UNIQUE_ID), config[CONF_NAME], config[CONF_ENTITIES]
82 config_entry: ConfigEntry,
83 async_add_entities: AddEntitiesCallback,
85 """Initialize Cover Group config entry."""
86 registry = er.async_get(hass)
87 entities = er.async_validate_entity_ids(
88 registry, config_entry.options[CONF_ENTITIES]
92 [
CoverGroup(config_entry.entry_id, config_entry.title, entities)]
98 hass: HomeAssistant, name: str, validated_config: dict[str, Any]
100 """Create a preview sensor."""
104 validated_config[CONF_ENTITIES],
109 """Representation of a CoverGroup."""
111 _attr_available: bool =
False
112 _attr_is_closed: bool |
None =
None
113 _attr_is_opening: bool |
None =
False
114 _attr_is_closing: bool |
None =
False
115 _attr_current_cover_position: int |
None = 100
117 def __init__(self, unique_id: str |
None, name: str, entities: list[str]) ->
None:
118 """Initialize a CoverGroup entity."""
120 self._covers: dict[str, set[str]] = {
121 KEY_OPEN_CLOSE: set(),
125 self._tilts: dict[str, set[str]] = {
126 KEY_OPEN_CLOSE: set(),
139 new_state: State |
None,
141 """Update dictionaries with supported features."""
143 for values
in self._covers.values():
144 values.discard(entity_id)
145 for values
in self._tilts.values():
146 values.discard(entity_id)
149 features = new_state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
151 if features & (CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE):
152 self._covers[KEY_OPEN_CLOSE].
add(entity_id)
154 self._covers[KEY_OPEN_CLOSE].discard(entity_id)
155 if features & (CoverEntityFeature.STOP):
156 self._covers[KEY_STOP].
add(entity_id)
158 self._covers[KEY_STOP].discard(entity_id)
159 if features & (CoverEntityFeature.SET_POSITION):
160 self._covers[KEY_POSITION].
add(entity_id)
162 self._covers[KEY_POSITION].discard(entity_id)
164 if features & (CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT):
165 self._tilts[KEY_OPEN_CLOSE].
add(entity_id)
167 self._tilts[KEY_OPEN_CLOSE].discard(entity_id)
168 if features & (CoverEntityFeature.STOP_TILT):
169 self._tilts[KEY_STOP].
add(entity_id)
171 self._tilts[KEY_STOP].discard(entity_id)
172 if features & (CoverEntityFeature.SET_TILT_POSITION):
173 self._tilts[KEY_POSITION].
add(entity_id)
175 self._tilts[KEY_POSITION].discard(entity_id)
178 """Move the covers up."""
179 data = {ATTR_ENTITY_ID: self._covers[KEY_OPEN_CLOSE]}
180 await self.
hasshass.services.async_call(
181 COVER_DOMAIN, SERVICE_OPEN_COVER, data, blocking=
True, context=self.
_context_context
185 """Move the covers down."""
186 data = {ATTR_ENTITY_ID: self._covers[KEY_OPEN_CLOSE]}
187 await self.
hasshass.services.async_call(
196 """Fire the stop action."""
197 data = {ATTR_ENTITY_ID: self._covers[KEY_STOP]}
198 await self.
hasshass.services.async_call(
199 COVER_DOMAIN, SERVICE_STOP_COVER, data, blocking=
True, context=self.
_context_context
203 """Set covers position."""
205 ATTR_ENTITY_ID: self._covers[KEY_POSITION],
206 ATTR_POSITION: kwargs[ATTR_POSITION],
208 await self.
hasshass.services.async_call(
210 SERVICE_SET_COVER_POSITION,
217 """Tilt covers open."""
218 data = {ATTR_ENTITY_ID: self._tilts[KEY_OPEN_CLOSE]}
219 await self.
hasshass.services.async_call(
221 SERVICE_OPEN_COVER_TILT,
228 """Tilt covers closed."""
229 data = {ATTR_ENTITY_ID: self._tilts[KEY_OPEN_CLOSE]}
230 await self.
hasshass.services.async_call(
232 SERVICE_CLOSE_COVER_TILT,
239 """Stop cover tilt."""
240 data = {ATTR_ENTITY_ID: self._tilts[KEY_STOP]}
241 await self.
hasshass.services.async_call(
243 SERVICE_STOP_COVER_TILT,
250 """Set tilt position."""
252 ATTR_ENTITY_ID: self._tilts[KEY_POSITION],
253 ATTR_TILT_POSITION: kwargs[ATTR_TILT_POSITION],
255 await self.
hasshass.services.async_call(
257 SERVICE_SET_COVER_TILT_POSITION,
265 """Update state and attributes."""
269 if (state := self.
hasshass.states.get(entity_id))
is not None
273 state
not in (STATE_UNKNOWN, STATE_UNAVAILABLE)
for state
in states
277 self.
_attr_available_attr_available = any(state != STATE_UNAVAILABLE
for state
in states)
283 if not (state := self.
hasshass.states.get(entity_id)):
285 if state.state == CoverState.OPEN:
288 if state.state == CoverState.CLOSED:
290 if state.state == CoverState.CLOSING:
293 if state.state == CoverState.OPENING:
300 position_covers = self._covers[KEY_POSITION]
301 all_position_states = [self.
hasshass.states.get(x)
for x
in position_covers]
302 position_states: list[State] =
list(filter(
None, all_position_states))
304 position_states, ATTR_CURRENT_POSITION
307 tilt_covers = self._tilts[KEY_POSITION]
308 all_tilt_states = [self.
hasshass.states.get(x)
for x
in tilt_covers]
309 tilt_states: list[State] =
list(filter(
None, all_tilt_states))
311 tilt_states, ATTR_CURRENT_TILT_POSITION
315 if self._covers[KEY_OPEN_CLOSE]:
316 supported_features |= CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
317 supported_features |= CoverEntityFeature.STOP
if self._covers[KEY_STOP]
else 0
318 if self._covers[KEY_POSITION]:
319 supported_features |= CoverEntityFeature.SET_POSITION
320 if self._tilts[KEY_OPEN_CLOSE]:
321 supported_features |= (
322 CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT
324 if self._tilts[KEY_STOP]:
325 supported_features |= CoverEntityFeature.STOP_TILT
326 if self._tilts[KEY_POSITION]:
327 supported_features |= CoverEntityFeature.SET_TILT_POSITION
None async_open_cover_tilt(self, **Any kwargs)
None async_set_cover_tilt_position(self, **Any kwargs)
None async_stop_cover_tilt(self, **Any kwargs)
None async_update_group_state(self)
_attr_current_cover_tilt_position
_attr_current_cover_position
None async_set_cover_position(self, **Any kwargs)
None async_open_cover(self, **Any kwargs)
None async_stop_cover(self, **Any kwargs)
_attr_extra_state_attributes
None async_update_supported_features(self, str entity_id, State|None new_state)
None __init__(self, str|None unique_id, str name, list[str] entities)
None async_close_cover_tilt(self, **Any kwargs)
None async_close_cover(self, **Any kwargs)
bool add(self, _T matcher)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
CoverGroup async_create_preview_cover(HomeAssistant hass, str name, dict[str, Any] validated_config)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Any reduce_attribute(list[State] states, str key, Any|None default=None, Callable[..., Any] reduce=mean_int)