@@ -30,7 +30,9 @@ def get_advanced_properties(self) -> List[Dict]:
3030 ).stdout
3131 return parse_powershell_list (ps_output )
3232
33- def get_advanced_property (self , advanced_property : str , use_registry : bool = False ) -> str :
33+ def get_advanced_property (
34+ self , advanced_property : str , use_registry : bool = False
35+ ) -> str :
3436 """
3537 Get specified interface advanced property.
3638
@@ -42,7 +44,11 @@ def get_advanced_property(self, advanced_property: str, use_registry: bool = Fal
4244 value = "RegistryValue" if use_registry else "DisplayValue"
4345
4446 properties = self .get_advanced_properties ()
45- found_property = [item for item in properties if advanced_property .lower () in item [name ].lower ()]
47+ found_property = [
48+ item
49+ for item in properties
50+ if advanced_property .lower () in item [name ].lower ()
51+ ]
4652
4753 if not found_property :
4854 raise UtilsException (f"Advanced Property { advanced_property } not found." )
@@ -65,7 +71,9 @@ def get_advanced_property_valid_values(self, registry_keyword: str) -> List:
6571 ).stdout
6672 return ps_output .strip ().split ()
6773
68- def set_advanced_property (self , registry_keyword : str , registry_value : Union [str , int ]) -> None :
74+ def set_advanced_property (
75+ self , registry_keyword : str , registry_value : Union [str , int ]
76+ ) -> None :
6977 """
7078 Set interface advanced property accessed by registry_keyword.
7179
@@ -92,6 +100,97 @@ def get_interface_index(self) -> str:
92100 :return: Read interface index
93101 """
94102 result = self ._connection .execute_powershell (
95- f"(Get-NetAdapter '{ self ._interface ().name } ').InterfaceIndex" , expected_return_codes = {0 }
103+ f"(Get-NetAdapter '{ self ._interface ().name } ').InterfaceIndex" ,
104+ expected_return_codes = {0 },
96105 )
97106 return result .stdout .strip ()
107+
108+ def get_phy_info (self , adapter_interface_description : str | None = None ) -> dict [str , str | bool ]:
109+ """
110+ Get PHY information and check auto-negotiation bits using WMI.
111+
112+ :param adapter_interface_description: Optional specific adapter description to match
113+ :return: Dictionary containing PHY information and auto-negotiation status
114+ :raises: UtilsException if PHY info command execution failed
115+ """
116+ cmd = (
117+ 'gwmi -namespace "root\\ WMI" IntlLan_GetPhyInfo -property InstanceName,PhyInfo | '
118+ 'Format-Table InstanceName, @{n="PhyInfo";e={($_ | select -expand PhyInfo) -join ","}} -auto'
119+ )
120+ result = self ._connection .execute_powershell (
121+ cmd , custom_exception = UtilsException
122+ )
123+ phy_data = {}
124+ phy_info_found = auto_neg_bits_detected = False
125+
126+ # Only process if we have output
127+ if result .stdout :
128+ lines = [
129+ line .strip ()
130+ for line in result .stdout .splitlines ()
131+ if line .strip ()
132+ and not any (x in line for x in ["InstanceName" , "PhyInfo" , "----" ])
133+ ]
134+
135+ # Normalize adapter name (strip "for ..." and trailing #N)
136+ base_name = None
137+ if adapter_interface_description :
138+ base_name = adapter_interface_description .split (" for " )[0 ].strip ()
139+ base_name = re .sub (r"\s+#\d+$" , "" , base_name ).strip ().lower ()
140+
141+ matching_line = None
142+ if base_name :
143+ for line in lines :
144+ # Extract full InstanceName part (everything before two or more spaces)
145+ instance_match = re .match (r"^(.*?)\s{2,}" , line )
146+ if not instance_match :
147+ continue
148+ instance_name = instance_match .group (1 ).strip ()
149+ normalized_name = (
150+ re .sub (r"\s+#\d+$" , "" , instance_name , flags = re .IGNORECASE )
151+ .strip ()
152+ .lower ()
153+ )
154+
155+ # Match exact normalized name
156+ if normalized_name == base_name :
157+ matching_line = line
158+ break
159+
160+ # Fallback if no exact match, use first line
161+ if not matching_line and lines :
162+ matching_line = lines [0 ]
163+
164+ if matching_line and "," in matching_line :
165+ # Extract the comma-separated PHY values (rightmost part)
166+ phy_info_str = matching_line .split (None , 1 )[- 1 ]
167+ if "," in phy_info_str :
168+ phy_values = phy_info_str .split ("," )
169+ phy_data = {"raw_values" : phy_values }
170+
171+ # Extract third value (auto-negotiation bits live here)
172+ if len (phy_values ) >= 3 :
173+ try :
174+ third_value = int (phy_values [2 ].strip ())
175+ auto_neg_bits = {
176+ f"bit_{ bit } " : bool (third_value & (1 << bit ))
177+ for bit in [0 , 1 ]
178+ }
179+ phy_data .update (
180+ {
181+ "third_value_decimal" : third_value ,
182+ "third_value_binary" : bin (third_value )[2 :],
183+ "auto_neg_bits" : auto_neg_bits ,
184+ }
185+ )
186+ phy_info_found = True
187+ auto_neg_bits_detected = any (auto_neg_bits .values ())
188+ except ValueError as e :
189+ phy_data ["parse_error" ] = str (e )
190+
191+ return {
192+ "phy_info_found" : phy_info_found ,
193+ "auto_neg_bits_detected" : auto_neg_bits_detected ,
194+ "phy_data" : phy_data ,
195+ "raw_output" : result .stdout or "" ,
196+ }
0 commit comments