@@ -23,14 +23,14 @@ async def _send_status(status: str, comment: str):
2323 }
2424 )
2525
26- async def _send_status_emulator (status : str , comment : str ):
26+ async def _send_status_emulator (udid : str , status : str , comment : str ):
2727 """Helper to send status responses for emulator category."""
2828 await send_response (
2929 {
3030 "action" : "status" ,
3131 "data" : {
3232 "category" : "iOSSimulator" ,
33- "name" : "Simulator" ,
33+ "name" : udid ,
3434 "status" : status ,
3535 "comment" : comment ,
3636 },
@@ -321,18 +321,37 @@ async def get_available_device_types() -> list[dict]:
321321
322322 data = json .loads (result .stdout )
323323 device_types = []
324-
324+
325325 for device_type in data .get ("devicetypes" , []):
326326 name = device_type .get ("name" , "" )
327327 identifier = device_type .get ("identifier" , "" )
328-
328+
329329 # Filter for iPhone and iPad devices only
330330 if "iPhone" in name or "iPad" in name :
331331 device_types .append ({
332332 "package" : identifier , # device type ID
333333 "version" : name , # device name
334334 "description" : "Apple" # OEM
335335 })
336+
337+ # Remove device types that already have a simulator created (don't show installed ones)
338+ try :
339+ simulators = await get_available_simulators ()
340+ installed_names = set ()
341+ for sim in simulators :
342+ sim_name = sim .get ("name" , "" )
343+ # Normalize by removing trailing parentheses (eg. "iPhone 16 (1)", "iPhone 16 (Default)")
344+ base_name = re .sub (r"\s*\(.*\)$" , "" , sim_name ).strip ()
345+ if base_name :
346+ installed_names .add (base_name )
347+
348+ # Compare using case-insensitive matching for robustness
349+ installed_names_lower = {n .lower () for n in installed_names }
350+ filtered_device_types = [dt for dt in device_types if dt .get ("version" , "" ).strip ().lower () not in installed_names_lower ]
351+ device_types = filtered_device_types
352+ except Exception :
353+ # If any error happens while checking simulators, fall back to showing all device types
354+ pass
336355
337356 return device_types
338357
@@ -651,7 +670,7 @@ async def launch_simulator(udid: str) -> bool:
651670
652671 # Boot simulator if not already booted
653672 if not is_already_booted :
654- await _send_status_emulator ("installed" , f"Launching simulator { simulator_name } ..." )
673+ await _send_status_emulator (udid , "installed" , f"Launching simulator { simulator_name } ..." )
655674 # Open Simulator app
656675 subprocess .Popen (
657676 ["open" , "-a" , "Simulator" ],
@@ -683,15 +702,15 @@ async def launch_simulator(udid: str) -> bool:
683702 })
684703 return False
685704
686- await _send_status_emulator ("installed" , f"Booting simulator: { simulator_name } ..." )
705+ await _send_status_emulator (udid , "installed" , f"Booting simulator: { simulator_name } ..." )
687706
688707 # Wait for boot to complete
689708 await asyncio .sleep (3 )
690709 else :
691- await _send_status_emulator ("installed" , f"Simulator { simulator_name } already running" )
710+ await _send_status_emulator (udid , "installed" , f"Simulator { simulator_name } already running" )
692711
693712 # Check if WebDriverAgent is installed on this simulator
694- await _send_status_emulator ("installed" , f"Checking WebDriverAgent installation on { simulator_name } ..." )
713+ await _send_status_emulator (udid , "installed" , f"Checking WebDriverAgent installation on { simulator_name } ..." )
695714 check_wda = subprocess .run (
696715 ["xcrun" , "simctl" , "get_app_container" , udid , "com.facebook.WebDriverAgentRunner.xctrunner" ],
697716 capture_output = True ,
@@ -701,7 +720,7 @@ async def launch_simulator(udid: str) -> bool:
701720 wda_installed = check_wda .returncode == 0 and check_wda .stdout .strip ()
702721
703722 if not wda_installed :
704- await _send_status_emulator ("installing" , f"WebDriverAgent not found on { simulator_name } , installing..." )
723+ await _send_status_emulator (udid , "installing" , f"WebDriverAgent not found on { simulator_name } , installing..." )
705724 await send_response ({
706725 "action" : "status" ,
707726 "data" : {
@@ -718,7 +737,7 @@ async def launch_simulator(udid: str) -> bool:
718737
719738 # Check if WebDriverAgent repo exists
720739 if not (webdriver_path / "WebDriverAgent.xcodeproj" ).exists ():
721- await _send_status_emulator ("installing" , "Cloning WebDriverAgent repository..." )
740+ await _send_status_emulator (udid , "installing" , "Cloning WebDriverAgent repository..." )
722741 if webdriver_path .exists ():
723742 shutil .rmtree (webdriver_path )
724743 webdriver_path .parent .mkdir (parents = True , exist_ok = True )
@@ -731,7 +750,7 @@ async def launch_simulator(udid: str) -> bool:
731750
732751 if clone_result .returncode != 0 :
733752 error_msg = f"Failed to clone WebDriverAgent: { clone_result .stderr } "
734- await _send_status_emulator ("installed" , error_msg )
753+ await _send_status_emulator (udid , "installed" , error_msg )
735754 # Continue with launching simulator even if WDA install fails
736755
737756 # Build and install WebDriverAgent for this simulator
@@ -741,11 +760,11 @@ async def launch_simulator(udid: str) -> bool:
741760 app_path_to_install = None
742761
743762 if standard_build_path .exists ():
744- await _send_status_emulator ("installed" , f"Found pre-built WebDriverAgent at { standard_build_path } " )
763+ await _send_status_emulator (udid , "installed" , f"Found pre-built WebDriverAgent at { standard_build_path } " )
745764 app_path_to_install = standard_build_path
746765 else :
747766 # Need to build
748- await _send_status_emulator ("installing" , f"Building WebDriverAgent for { simulator_name } ..." )
767+ await _send_status_emulator (udid , "installing" , f"Building WebDriverAgent for { simulator_name } ..." )
749768
750769 with tempfile .TemporaryDirectory () as derived_data_path :
751770 build_cmd = [
@@ -776,13 +795,13 @@ async def launch_simulator(udid: str) -> bool:
776795 print (f"[simulator] Copied built app to { standard_build_path } " )
777796 app_path_to_install = standard_build_path
778797 else :
779- await _send_status_emulator ("installed" , f"WebDriverAgent app not found at { app_path } " )
798+ await _send_status_emulator (udid , "installed" , f"WebDriverAgent app not found at { app_path } " )
780799 else :
781- await _send_status_emulator ("installed" , f"WebDriverAgent build failed: { build_result .stderr [- 500 :]} " )
800+ await _send_status_emulator (udid , "installed" , f"WebDriverAgent build failed: { build_result .stderr [- 500 :]} " )
782801
783802 # Install the app if we have it
784803 if app_path_to_install :
785- await _send_status_emulator ("installing" , f"Installing WebDriverAgent on { simulator_name } ..." )
804+ await _send_status_emulator (udid , "installing" , f"Installing WebDriverAgent on { simulator_name } ..." )
786805 install_result = subprocess .run (
787806 ["xcrun" , "simctl" , "install" , udid , str (app_path_to_install )],
788807 capture_output = True ,
@@ -799,7 +818,7 @@ async def launch_simulator(udid: str) -> bool:
799818
800819 # Launch WebDriverAgent if installed
801820 if wda_installed :
802- await _send_status_emulator ("installed" , f"Launching WebDriverAgent on { simulator_name } ..." )
821+ await _send_status_emulator (udid , "installed" , f"Launching WebDriverAgent on { simulator_name } ..." )
803822 await send_response ({
804823 "action" : "status" ,
805824 "data" : {
@@ -926,7 +945,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
926945 "action" : "status" ,
927946 "data" : {
928947 "category" : "iOSSimulator" ,
929- "name " : device_name ,
948+ "package " : device_type_id ,
930949 "status" : "not installed" ,
931950 "comment" : error_msg ,
932951 }
@@ -942,7 +961,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
942961 "action" : "status" ,
943962 "data" : {
944963 "category" : "iOSSimulator" ,
945- "name " : device_name ,
964+ "package " : device_type_id ,
946965 "status" : "not installed" ,
947966 "comment" : error_msg ,
948967 }
@@ -972,7 +991,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
972991 "action" : "status" ,
973992 "data" : {
974993 "category" : "iOSSimulator" ,
975- "name " : device_name ,
994+ "package " : device_type_id ,
976995 "status" : "installing" ,
977996 "comment" : f"Creating { simulator_name } with { runtime_name } ..." ,
978997 }
@@ -993,7 +1012,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
9931012 "action" : "status" ,
9941013 "data" : {
9951014 "category" : "iOSSimulator" ,
996- "name " : device_name ,
1015+ "package " : device_type_id ,
9971016 "status" : "not installed" ,
9981017 "comment" : error_msg ,
9991018 }
@@ -1008,7 +1027,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
10081027 "action" : "status" ,
10091028 "data" : {
10101029 "category" : "iOSSimulator" ,
1011- "name " : device_name ,
1030+ "package " : device_type_id ,
10121031 "status" : "installed" ,
10131032 "comment" : f"Simulator '{ simulator_name } ' created successfully ({ new_udid } )" ,
10141033 }
@@ -1023,7 +1042,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
10231042 "action" : "status" ,
10241043 "data" : {
10251044 "category" : "iOSSimulator" ,
1026- "name " : device_name if 'device_name' in locals () else "Unknown" ,
1045+ "package " : device_type_id ,
10271046 "status" : "error" ,
10281047 "comment" : error_msg ,
10291048 }
@@ -1038,7 +1057,7 @@ async def create_simulator_from_device_type(device_param: str) -> bool:
10381057 "action" : "status" ,
10391058 "data" : {
10401059 "category" : "iOSSimulator" ,
1041- "name " : device_name if 'device_name' in locals () else "Unknown" ,
1060+ "package " : device_type_id ,
10421061 "status" : "error" ,
10431062 "comment" : error_msg ,
10441063 }
0 commit comments