2222 DvCoordinator ,
2323 EnergyCoordinator ,
2424 SdhbHub ,
25+ ShutterCommonCoordinator ,
2526 WxCoordinator ,
2627 ZonesCoordinator ,
2728)
@@ -42,9 +43,31 @@ def _platforms(entry: ConfigEntry) -> list[str]:
4243 return const .PLATFORMS_ZONES
4344 if module == const .MODULE_ENERGY :
4445 return const .PLATFORMS_ENERGY
46+ if module == const .MODULE_SHUTTER_COMMON :
47+ return const .PLATFORMS_SHUTTER_COMMON
4548 return const .PLATFORMS_VMC
4649
4750
51+ async def _ensure_common_entry (hass : HomeAssistant ) -> None :
52+ """Auto-create the "Dynamic Shutter · Común" singleton (once), if missing.
53+
54+ Called when a shutter is set up. Guarded by a flag (several shutters set up
55+ at once) and by the flow's unique-id singleton, so only one entry is created.
56+ On restart the entry already exists and this is a no-op.
57+ """
58+ if any (e .data .get (const .CONF_MODULE ) == const .MODULE_SHUTTER_COMMON
59+ for e in hass .config_entries .async_entries (const .DOMAIN )):
60+ return
61+ if hass .data [const .DOMAIN ].get ("_common_creating" ):
62+ return
63+ hass .data [const .DOMAIN ]["_common_creating" ] = True
64+ try :
65+ await hass .config_entries .flow .async_init (
66+ const .DOMAIN , context = {"source" : "shutter_common" })
67+ finally :
68+ hass .data [const .DOMAIN ].pop ("_common_creating" , None )
69+
70+
4871async def async_setup_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
4972 """Set up Dynamic Home from a config entry."""
5073 hub : SdhbHub = hass .data .setdefault (const .DOMAIN , {}).setdefault (
@@ -79,6 +102,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
79102 elif module == const .MODULE_ENERGY :
80103 coordinator = EnergyCoordinator (hass , entry ) # F34 house energy context
81104 coordinator .async_setup_listeners ()
105+ elif module == const .MODULE_SHUTTER_COMMON :
106+ coordinator = ShutterCommonCoordinator (hass , entry ) # shared screen + globals
82107 else :
83108 coordinator = DvCoordinator (hass , entry , hub )
84109 coordinator .async_setup_listeners ()
@@ -92,9 +117,31 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
92117 await hass .config_entries .async_forward_entry_setups (entry , _platforms (entry ))
93118 entry .async_on_unload (entry .add_update_listener (_async_options_updated ))
94119 _async_register_services (hass )
120+ # First shutter -> make sure the shared "Dynamic Shutter · Común" entry exists.
121+ # Background task tied to the entry so it's cancelled on unload (no lingering).
122+ if module == const .MODULE_SHUTTER :
123+ entry .async_create_background_task (
124+ hass , _ensure_common_entry (hass ), "dynamic_home ensure common" )
95125 return True
96126
97127
128+ async def async_remove_entry (hass : HomeAssistant , entry : ConfigEntry ) -> None :
129+ """A config entry was deleted (not just unloaded). If the LAST shutter is
130+ gone, remove the auto-created "Común" singleton too — it has no reason to
131+ linger without any shutter. Only fires on real removal, never on restart.
132+ """
133+ if entry .data .get (const .CONF_MODULE ) != const .MODULE_SHUTTER :
134+ return
135+ shutters = [e for e in hass .config_entries .async_entries (const .DOMAIN )
136+ if e .data .get (const .CONF_MODULE ) == const .MODULE_SHUTTER
137+ and e .entry_id != entry .entry_id ]
138+ if shutters :
139+ return
140+ for e in hass .config_entries .async_entries (const .DOMAIN ):
141+ if e .data .get (const .CONF_MODULE ) == const .MODULE_SHUTTER_COMMON :
142+ hass .async_create_task (hass .config_entries .async_remove (e .entry_id ))
143+
144+
98145async def async_unload_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
99146 """Unload a config entry."""
100147 unloaded = await hass .config_entries .async_unload_platforms (
@@ -123,16 +170,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
123170 ph = hass .data [const .DOMAIN ].get ("_peak_ds" )
124171 if ph is not None :
125172 ph .clear (entry .entry_id )
126- # This entry's adder is gone with its platform.
127- adders = hass .data [const .DOMAIN ].get ("_ds_summary_adders" ) or {}
128- adders .pop (entry .entry_id , None )
129- # If this entry owned the house-wide shared sensors (counts + sun),
130- # release the marker; the next tick of any surviving DS coordinator
131- # adopts them (see DsCoordinator._async_update_data) — they used to
132- # simply vanish until a restart.
133- if (hass .data [const .DOMAIN ].get (const .DATA_DS_SUMMARY_OWNER )
134- == entry .entry_id ):
135- hass .data [const .DOMAIN ].pop (const .DATA_DS_SUMMARY_OWNER , None )
136173 # A VMC must not leave repair issues for a removed entry (F08 filter,
137174 # free-cooling advisory).
138175 if isinstance (coordinator , DvCoordinator ):
@@ -156,7 +193,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
156193 if not _entry_ids (hass ):
157194 _async_unregister_services (hass )
158195 for key in ("_hub" , "_anticycle" , "_peak_dc" , "_peak_ds" ,
159- "_shared_emit" , "_facades" , "_ds_summary_adders" ):
196+ "_shared_emit" , "_facades" ):
160197 hass .data [const .DOMAIN ].pop (key , None )
161198 return unloaded
162199
0 commit comments