@@ -166,6 +166,8 @@ def valid_apps(self) -> set[str]:
166166 def start (self ) -> None :
167167 self .logger .debug ("Starting the app management subsystem" )
168168 if self .AD .apps_enabled :
169+ self .AD .loop .create_task (self .init_admin_entities ())
170+
169171 task = self .AD .loop .create_task (
170172 self .check_app_updates (mode = UpdateMode .INIT ),
171173 name = "check_app_updates" ,
@@ -202,6 +204,21 @@ async def get_state(self, name: str, **kwargs):
202204
203205 return await self .AD .state .get_state ("_app_management" , "admin" , entity_id , ** kwargs )
204206
207+ async def init_admin_entities (self ):
208+ for app_name , cfg in self .app_config .root .items ():
209+ match cfg :
210+ case AppConfig () as app_cfg :
211+ await self .add_entity (
212+ app_name ,
213+ state = "loaded" ,
214+ attributes = {
215+ "totalcallbacks" : 0 ,
216+ "instancecallbacks" : 0 ,
217+ "args" : app_cfg .args ,
218+ "config_path" : app_cfg .config_path ,
219+ },
220+ )
221+
205222 async def add_entity (self , name : str , state , attributes ):
206223 # not a fully qualified entity name
207224 if "." not in name :
@@ -715,7 +732,7 @@ async def check_app_config_files(self, update_actions: UpdateActions):
715732 threads_to_add = active_apps - self .AD .threading .thread_count
716733 self .logger .debug (f"Adding { threads_to_add } threads based on the active app count" )
717734 for _ in range (threads_to_add ):
718- await self .AD .threading .add_thread (silent = False , pinthread = True )
735+ await self .AD .threading .add_thread (silent = False )
719736
720737 @utils .executor_decorator
721738 def read_config_file (self , file : Path ) -> AllAppConfig :
@@ -814,7 +831,6 @@ async def wrapper(*args, **kwargs):
814831
815832 return wrapper
816833
817- # @utils.timeit
818834 async def check_app_updates (
819835 self ,
820836 plugin_ns : str | None = None ,
@@ -1371,7 +1387,12 @@ def enable_app(self, app: str):
13711387
13721388 @contextlib .asynccontextmanager
13731389 async def app_run_context (self , app : str , ** kwargs ):
1374- """Context manager for running an app."""
1390+ """Context manager for running an app to help during testing.
1391+
1392+ Args:
1393+ app (str): The name of the app to run. Must have an entry in the app_config root.
1394+ **kwargs: Arbitrary keyword arguments representing configuration fields to temporarily update the app with.
1395+ """
13751396 match self .app_config .root .get (app ):
13761397 case AppConfig () as app_cfg :
13771398 # Store the complete original configuration
@@ -1382,14 +1403,21 @@ async def app_run_context(self, app: str, **kwargs):
13821403 return
13831404
13841405 try :
1385- self .update_app (app , ** kwargs )
1406+ if kwargs :
1407+ self .update_app (app , ** kwargs )
1408+ self .logger .debug ("Temporarily updated app '%s' with: %s" , app , kwargs )
1409+
1410+ # Ensure there's at least one thread available
1411+ if not self .AD .threading .thread_count :
1412+ await self .AD .threading .create_initial_threads ()
13861413
1414+ created_app_object = False
13871415 if app not in self .objects :
13881416 self .logger .debug ("Creating ManagedObject for app '%s'" , app )
13891417 await self .create_app_object (app )
13901418 await self .AD .threading .calculate_pin_threads ()
1419+ created_app_object = True
13911420
1392- self .logger .debug ("Temporarily updated app '%s' with: %s" , app , kwargs )
13931421 await self .start_app (app )
13941422 yield
13951423 finally :
@@ -1399,6 +1427,8 @@ async def app_run_context(self, app: str, **kwargs):
13991427 self .logger .debug ("Restored app '%s' to original state" , app )
14001428 except ValidationError as e :
14011429 self .logger .warning ("Failed to restore app '%s' to original state: %s" , app , e )
1430+ if created_app_object :
1431+ self .objects .pop (app )
14021432
14031433 @utils .executor_decorator
14041434 def remove_app (self , app : str , ** kwargs ):
0 commit comments