@@ -140,9 +140,7 @@ def __init__(self, coresys: CoreSys, slug: str):
140140 super ().__init__ (coresys , slug )
141141 self .instance : DockerAddon = DockerAddon (coresys , self )
142142 self ._state : AddonState = AddonState .UNKNOWN
143- self ._manual_stop : bool = (
144- self .sys_hardware .helper .last_boot != self .sys_config .last_boot
145- )
143+ self ._manual_stop : bool = False
146144 self ._listeners : list [EventListener ] = []
147145 self ._startup_event = asyncio .Event ()
148146 self ._startup_task : asyncio .Task | None = None
@@ -216,6 +214,10 @@ def in_progress(self) -> bool:
216214
217215 async def load (self ) -> None :
218216 """Async initialize of object."""
217+ self ._manual_stop = (
218+ await self .sys_hardware .helper .last_boot () != self .sys_config .last_boot
219+ )
220+
219221 if self .is_detached :
220222 await super ().refresh_path_cache ()
221223
@@ -720,7 +722,7 @@ async def write_options(self) -> None:
720722
721723 try :
722724 options = self .schema .validate (self .options )
723- write_json_file ( self .path_options , options )
725+ await self . sys_run_in_executor ( write_json_file , self .path_options , options )
724726 except vol .Invalid as ex :
725727 _LOGGER .error (
726728 "Add-on %s has invalid options: %s" ,
@@ -751,9 +753,12 @@ async def unload(self) -> None:
751753 for listener in self ._listeners :
752754 self .sys_bus .remove_listener (listener )
753755
754- if self .path_data .is_dir ():
755- _LOGGER .info ("Removing add-on data folder %s" , self .path_data )
756- await remove_data (self .path_data )
756+ def remove_data_dir ():
757+ if self .path_data .is_dir ():
758+ _LOGGER .info ("Removing add-on data folder %s" , self .path_data )
759+ remove_data (self .path_data )
760+
761+ await self .sys_run_in_executor (remove_data_dir )
757762
758763 async def _check_ingress_port (self ):
759764 """Assign a ingress port if dynamic port selection is used."""
@@ -775,11 +780,14 @@ async def install(self) -> None:
775780 await self .sys_addons .data .install (self .addon_store )
776781 await self .load ()
777782
778- if not self .path_data .is_dir ():
779- _LOGGER .info (
780- "Creating Home Assistant add-on data folder %s" , self .path_data
781- )
782- self .path_data .mkdir ()
783+ def setup_data ():
784+ if not self .path_data .is_dir ():
785+ _LOGGER .info (
786+ "Creating Home Assistant add-on data folder %s" , self .path_data
787+ )
788+ self .path_data .mkdir ()
789+
790+ await self .sys_run_in_executor (setup_data )
783791
784792 # Setup/Fix AppArmor profile
785793 await self .install_apparmor ()
@@ -818,14 +826,17 @@ async def uninstall(
818826
819827 await self .unload ()
820828
821- # Remove config if present and requested
822- if self .addon_config_used and remove_config :
823- await remove_data (self .path_config )
829+ def cleanup_config_and_audio ():
830+ # Remove config if present and requested
831+ if self .addon_config_used and remove_config :
832+ remove_data (self .path_config )
833+
834+ # Cleanup audio settings
835+ if self .path_pulse .exists ():
836+ with suppress (OSError ):
837+ self .path_pulse .unlink ()
824838
825- # Cleanup audio settings
826- if self .path_pulse .exists ():
827- with suppress (OSError ):
828- self .path_pulse .unlink ()
839+ await self .sys_run_in_executor (cleanup_config_and_audio )
829840
830841 # Cleanup AppArmor profile
831842 with suppress (HostAppArmorError ):
@@ -938,19 +949,20 @@ async def rebuild(self) -> asyncio.Task | None:
938949 )
939950 return out
940951
941- def write_pulse (self ) -> None :
952+ async def write_pulse (self ) -> None :
942953 """Write asound config to file and return True on success."""
943954 pulse_config = self .sys_plugins .audio .pulse_client (
944955 input_profile = self .audio_input , output_profile = self .audio_output
945956 )
946957
947- # Cleanup wrong maps
948- if self .path_pulse .is_dir ():
949- shutil .rmtree (self .path_pulse , ignore_errors = True )
958+ def write_pulse_config ():
959+ # Cleanup wrong maps
960+ if self .path_pulse .is_dir ():
961+ shutil .rmtree (self .path_pulse , ignore_errors = True )
962+ self .path_pulse .write_text (pulse_config , encoding = "utf-8" )
950963
951- # Write pulse config
952964 try :
953- self .path_pulse . write_text ( pulse_config , encoding = "utf-8" )
965+ await self .sys_run_in_executor ( write_pulse_config )
954966 except OSError as err :
955967 if err .errno == errno .EBADMSG :
956968 self .sys_resolution .unhealthy = UnhealthyReason .OSERROR_BAD_MESSAGE
@@ -965,7 +977,7 @@ def write_pulse(self) -> None:
965977 async def install_apparmor (self ) -> None :
966978 """Install or Update AppArmor profile for Add-on."""
967979 exists_local = self .sys_host .apparmor .exists (self .slug )
968- exists_addon = self .path_apparmor .exists ( )
980+ exists_addon = await self .sys_run_in_executor ( self . path_apparmor .exists )
969981
970982 # Nothing to do
971983 if not exists_local and not exists_addon :
@@ -1070,7 +1082,7 @@ async def start(self) -> asyncio.Task:
10701082
10711083 # Sound
10721084 if self .with_audio :
1073- self .write_pulse ()
1085+ await self .write_pulse ()
10741086
10751087 def _check_addon_config_dir ():
10761088 if self .path_config .is_dir ():
@@ -1441,6 +1453,12 @@ def _extract_tarfile() -> tuple[TemporaryDirectory, dict[str, Any]]:
14411453 # Restore data and config
14421454 def _restore_data ():
14431455 """Restore data and config."""
1456+ _LOGGER .info ("Restoring data and config for addon %s" , self .slug )
1457+ if self .path_data .is_dir ():
1458+ remove_data (self .path_data )
1459+ if self .path_config .is_dir ():
1460+ remove_data (self .path_config )
1461+
14441462 temp_data = Path (tmp .name , "data" )
14451463 if temp_data .is_dir ():
14461464 shutil .copytree (temp_data , self .path_data , symlinks = True )
@@ -1453,12 +1471,6 @@ def _restore_data():
14531471 elif self .addon_config_used :
14541472 self .path_config .mkdir ()
14551473
1456- _LOGGER .info ("Restoring data and config for addon %s" , self .slug )
1457- if self .path_data .is_dir ():
1458- await remove_data (self .path_data )
1459- if self .path_config .is_dir ():
1460- await remove_data (self .path_config )
1461-
14621474 try :
14631475 await self .sys_run_in_executor (_restore_data )
14641476 except shutil .Error as err :
@@ -1468,7 +1480,7 @@ def _restore_data():
14681480
14691481 # Restore AppArmor
14701482 profile_file = Path (tmp .name , "apparmor.txt" )
1471- if profile_file .exists ( ):
1483+ if await self . sys_run_in_executor ( profile_file .exists ):
14721484 try :
14731485 await self .sys_host .apparmor .load_profile (
14741486 self .slug , profile_file
@@ -1489,7 +1501,7 @@ def _restore_data():
14891501 if data [ATTR_STATE ] == AddonState .STARTED :
14901502 wait_for_start = await self .start ()
14911503 finally :
1492- tmp .cleanup ( )
1504+ await self . sys_run_in_executor ( tmp .cleanup )
14931505 _LOGGER .info ("Finished restore for add-on %s" , self .slug )
14941506 return wait_for_start
14951507
0 commit comments