Collect Wi-Fi interface metadata using nmcli + ip link fallbacks.
(default_interface: str = 'wlan0')
| 146 | |
| 147 | |
| 148 | def gather_wifi_interfaces(default_interface: str = 'wlan0') -> List[Dict]: |
| 149 | """Collect Wi-Fi interface metadata using nmcli + ip link fallbacks.""" |
| 150 | # Resolve 'auto' to actual interface name |
| 151 | if default_interface == 'auto': |
| 152 | from shared import detect_wifi_interface |
| 153 | default_interface = detect_wifi_interface('auto') |
| 154 | interfaces: Dict[str, Dict] = {} |
| 155 | |
| 156 | try: |
| 157 | nmcli_result = subprocess.run( |
| 158 | ['nmcli', '-t', '-f', 'DEVICE,TYPE,STATE,CONNECTION', 'dev', 'status'], |
| 159 | capture_output=True, |
| 160 | text=True, |
| 161 | timeout=5 |
| 162 | ) |
| 163 | if nmcli_result.returncode == 0: |
| 164 | for line in nmcli_result.stdout.strip().split('\n'): |
| 165 | if not line: |
| 166 | continue |
| 167 | parts = line.split(':', 3) |
| 168 | if len(parts) < 4: |
| 169 | continue |
| 170 | device, dev_type, state, connection = parts |
| 171 | if dev_type != 'wifi': |
| 172 | continue |
| 173 | normalized_connection = connection if connection and connection != '--' else None |
| 174 | interfaces[device] = { |
| 175 | 'name': device, |
| 176 | 'state': state or 'UNKNOWN', |
| 177 | 'is_default': device == default_interface, |
| 178 | 'connected_ssid': normalized_connection, |
| 179 | 'connection': normalized_connection, |
| 180 | 'connected': (state or '').lower() == 'connected' and bool(normalized_connection), |
| 181 | } |
| 182 | except Exception as exc: |
| 183 | logger.debug(f"nmcli dev status failed: {exc}") |
| 184 | |
| 185 | try: |
| 186 | ip_result = subprocess.run( |
| 187 | ['ip', '-o', 'link', 'show'], |
| 188 | capture_output=True, |
| 189 | text=True, |
| 190 | timeout=5 |
| 191 | ) |
| 192 | if ip_result.returncode == 0: |
| 193 | for line in ip_result.stdout.strip().split('\n'): |
| 194 | if not line: |
| 195 | continue |
| 196 | name_match = re.match(r'\d+:\s+(\S+):', line) |
| 197 | if not name_match: |
| 198 | continue |
| 199 | iface_name = name_match.group(1) |
| 200 | if not _WIFI_NAME_PATTERN.match(iface_name): |
| 201 | continue |
| 202 | entry = interfaces.setdefault(iface_name, { |
| 203 | 'name': iface_name, |
| 204 | 'state': 'UNKNOWN', |
| 205 | 'is_default': iface_name == default_interface, |
no test coverage detected