33from __future__ import annotations
44
55import asyncio
6+ from dataclasses import dataclass
67from typing import Any , cast
78
89from aioshelly .block_device import Block
1314 ATTR_TILT_POSITION ,
1415 CoverDeviceClass ,
1516 CoverEntity ,
17+ CoverEntityDescription ,
1618 CoverEntityFeature ,
1719)
1820from homeassistant .core import HomeAssistant , callback
1921from homeassistant .helpers .entity_platform import AddConfigEntryEntitiesCallback
2022
2123from .const import RPC_COVER_UPDATE_TIME_SEC
2224from .coordinator import ShellyBlockCoordinator , ShellyConfigEntry , ShellyRpcCoordinator
23- from .entity import ShellyBlockEntity , ShellyRpcEntity
24- from .utils import get_device_entry_gen , get_rpc_key_ids
25+ from .entity import (
26+ BlockEntityDescription ,
27+ RpcEntityDescription ,
28+ ShellyBlockAttributeEntity ,
29+ ShellyRpcAttributeEntity ,
30+ async_setup_entry_attribute_entities ,
31+ async_setup_entry_rpc ,
32+ )
33+ from .utils import get_device_entry_gen
2534
2635PARALLEL_UPDATES = 0
2736
2837
38+ @dataclass (frozen = True , kw_only = True )
39+ class BlockCoverDescription (BlockEntityDescription , CoverEntityDescription ):
40+ """Class to describe a BLOCK cover."""
41+ 42+ 43+ @dataclass (frozen = True , kw_only = True )
44+ class RpcCoverDescription (RpcEntityDescription , CoverEntityDescription ):
45+ """Class to describe a RPC cover."""
46+ 47+ 48+ BLOCK_COVERS = {
49+ ("roller" , "roller" ): BlockCoverDescription (
50+ key = "roller|roller" ,
51+ )
52+ }
53+ 54+ RPC_COVERS = {
55+ "cover" : RpcCoverDescription (
56+ key = "cover" ,
57+ ),
58+ }
59+ 60+ 2961async def async_setup_entry (
3062 hass : HomeAssistant ,
3163 config_entry : ShellyConfigEntry ,
@@ -46,13 +78,11 @@ def async_setup_block_entry(
4678) -> None :
4779 """Set up cover for device."""
4880 coordinator = config_entry .runtime_data .block
49- assert coordinator and coordinator .device .blocks
50- blocks = [block for block in coordinator .device .blocks if block .type == "roller" ]
51- 52- if not blocks :
53- return
81+ assert coordinator
5482
55- async_add_entities (BlockShellyCover (coordinator , block ) for block in blocks )
83+ async_setup_entry_attribute_entities (
84+ hass , config_entry , async_add_entities , BLOCK_COVERS , BlockShellyCover
85+ )
5686
5787
5888@callback
@@ -64,26 +94,32 @@ def async_setup_rpc_entry(
6494 """Set up entities for RPC device."""
6595 coordinator = config_entry .runtime_data .rpc
6696 assert coordinator
67- cover_key_ids = get_rpc_key_ids (coordinator .device .status , "cover" )
68- 69- if not cover_key_ids :
70- return
7197
72- async_add_entities (RpcShellyCover (coordinator , id_ ) for id_ in cover_key_ids )
98+ async_setup_entry_rpc (
99+ hass , config_entry , async_add_entities , RPC_COVERS , RpcShellyCover
100+ )
73101
74102
75- class BlockShellyCover (ShellyBlockEntity , CoverEntity ):
103+ class BlockShellyCover (ShellyBlockAttributeEntity , CoverEntity ):
76104 """Entity that controls a cover on block based Shelly devices."""
77105
106+ entity_description : BlockCoverDescription
78107 _attr_device_class = CoverDeviceClass .SHUTTER
79108 _attr_supported_features : CoverEntityFeature = (
80109 CoverEntityFeature .OPEN | CoverEntityFeature .CLOSE | CoverEntityFeature .STOP
81110 )
82111
83- def __init__ (self , coordinator : ShellyBlockCoordinator , block : Block ) -> None :
112+ def __init__ (
113+ self ,
114+ coordinator : ShellyBlockCoordinator ,
115+ block : Block ,
116+ attribute : str ,
117+ description : BlockCoverDescription ,
118+ ) -> None :
84119 """Initialize block cover."""
85- super ().__init__ (coordinator , block )
120+ super ().__init__ (coordinator , block , attribute , description )
86121 self .control_result : dict [str , Any ] | None = None
122+ self ._attr_unique_id : str = f"{ coordinator .mac } -{ block .description } "
87123 if self .coordinator .device .settings ["rollers" ][0 ]["positioning" ]:
88124 self ._attr_supported_features |= CoverEntityFeature .SET_POSITION
89125
@@ -148,22 +184,29 @@ def _update_callback(self) -> None:
148184 super ()._update_callback ()
149185
150186
151- class RpcShellyCover (ShellyRpcEntity , CoverEntity ):
187+ class RpcShellyCover (ShellyRpcAttributeEntity , CoverEntity ):
152188 """Entity that controls a cover on RPC based Shelly devices."""
153189
190+ entity_description : RpcCoverDescription
154191 _attr_device_class = CoverDeviceClass .SHUTTER
155192 _attr_supported_features : CoverEntityFeature = (
156193 CoverEntityFeature .OPEN | CoverEntityFeature .CLOSE | CoverEntityFeature .STOP
157194 )
158195
159- def __init__ (self , coordinator : ShellyRpcCoordinator , id_ : int ) -> None :
196+ def __init__ (
197+ self ,
198+ coordinator : ShellyRpcCoordinator ,
199+ key : str ,
200+ attribute : str ,
201+ description : RpcCoverDescription ,
202+ ) -> None :
160203 """Initialize rpc cover."""
161- super ().__init__ (coordinator , f"cover: { id_ } " )
162- self ._id = id_
204+ super ().__init__ (coordinator , key , attribute , description )
205+ self ._attr_unique_id : str = f" { coordinator . mac } - { key } "
163206 self ._update_task : asyncio .Task | None = None
164207 if self .status ["pos_control" ]:
165208 self ._attr_supported_features |= CoverEntityFeature .SET_POSITION
166- if coordinator .device .config [f"cover: { id_ } " ].get ("slat" , {}).get ("enable" ):
209+ if coordinator .device .config [key ].get ("slat" , {}).get ("enable" ):
167210 self ._attr_supported_features |= (
168211 CoverEntityFeature .OPEN_TILT
169212 | CoverEntityFeature .CLOSE_TILT
0 commit comments