autoglm-gui 1.0.1__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,8 @@
2
2
 
3
3
  from fastapi import APIRouter
4
4
 
5
- from AutoGLM_GUI.adb_plus import get_wifi_ip, get_device_serial
5
+ from AutoGLM_GUI.adb_plus import get_wifi_ip, get_device_serial, pair_device
6
+ from AutoGLM_GUI.adb_plus.qr_pair import qr_pairing_manager
6
7
 
7
8
  from AutoGLM_GUI.schemas import (
8
9
  DeviceListResponse,
@@ -12,6 +13,13 @@ from AutoGLM_GUI.schemas import (
12
13
  WiFiDisconnectResponse,
13
14
  WiFiManualConnectRequest,
14
15
  WiFiManualConnectResponse,
16
+ WiFiPairRequest,
17
+ WiFiPairResponse,
18
+ MdnsDiscoverResponse,
19
+ MdnsDeviceResponse,
20
+ QRPairGenerateResponse,
21
+ QRPairStatusResponse,
22
+ QRPairCancelResponse,
15
23
  )
16
24
  from AutoGLM_GUI.state import agents
17
25
 
@@ -166,3 +174,218 @@ def connect_wifi_manual(
166
174
  message=f"Successfully connected to {address}",
167
175
  device_id=address,
168
176
  )
177
+
178
+
179
+ @router.post("/api/devices/pair_wifi", response_model=WiFiPairResponse)
180
+ def pair_wifi(request: WiFiPairRequest) -> WiFiPairResponse:
181
+ """使用无线调试配对并连接到 WiFi 设备 (Android 11+)."""
182
+ import re
183
+
184
+ from phone_agent.adb import ADBConnection
185
+
186
+ # IP 格式验证
187
+ ip_pattern = r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$"
188
+ if not re.match(ip_pattern, request.ip):
189
+ return WiFiPairResponse(
190
+ success=False,
191
+ message="Invalid IP address format",
192
+ error="invalid_ip",
193
+ )
194
+
195
+ # 配对端口验证
196
+ if not (1 <= request.pairing_port <= 65535):
197
+ return WiFiPairResponse(
198
+ success=False,
199
+ message="Pairing port must be between 1 and 65535",
200
+ error="invalid_port",
201
+ )
202
+
203
+ # 连接端口验证
204
+ if not (1 <= request.connection_port <= 65535):
205
+ return WiFiPairResponse(
206
+ success=False,
207
+ message="Connection port must be between 1 and 65535",
208
+ error="invalid_port",
209
+ )
210
+
211
+ # 配对码验证 (6 位数字)
212
+ if not request.pairing_code.isdigit() or len(request.pairing_code) != 6:
213
+ return WiFiPairResponse(
214
+ success=False,
215
+ message="Pairing code must be 6 digits",
216
+ error="invalid_pairing_code",
217
+ )
218
+
219
+ conn = ADBConnection()
220
+
221
+ # 步骤 1: 配对设备
222
+ ok, msg = pair_device(
223
+ ip=request.ip,
224
+ port=request.pairing_port,
225
+ pairing_code=request.pairing_code,
226
+ adb_path=conn.adb_path,
227
+ )
228
+
229
+ if not ok:
230
+ return WiFiPairResponse(
231
+ success=False,
232
+ message=msg,
233
+ error="pair_failed",
234
+ )
235
+
236
+ # 步骤 2: 使用标准 ADB 端口连接到设备
237
+ connection_address = f"{request.ip}:{request.connection_port}"
238
+ ok, connect_msg = conn.connect(connection_address)
239
+
240
+ if not ok:
241
+ return WiFiPairResponse(
242
+ success=False,
243
+ message=f"Paired successfully but connection failed: {connect_msg}",
244
+ error="connect_failed",
245
+ )
246
+
247
+ return WiFiPairResponse(
248
+ success=True,
249
+ message=f"Successfully paired and connected to {connection_address}",
250
+ device_id=connection_address,
251
+ )
252
+
253
+
254
+ @router.get("/api/devices/discover_mdns", response_model=MdnsDiscoverResponse)
255
+ def discover_mdns() -> MdnsDiscoverResponse:
256
+ """Discover wireless ADB devices via mDNS."""
257
+ from phone_agent.adb import ADBConnection
258
+ from AutoGLM_GUI.adb_plus import discover_mdns_devices
259
+
260
+ try:
261
+ conn = ADBConnection()
262
+ devices = discover_mdns_devices(conn.adb_path)
263
+
264
+ device_responses = [
265
+ MdnsDeviceResponse(
266
+ name=dev.name,
267
+ ip=dev.ip,
268
+ port=dev.port,
269
+ has_pairing=dev.has_pairing,
270
+ service_type=dev.service_type,
271
+ pairing_port=dev.pairing_port,
272
+ )
273
+ for dev in devices
274
+ ]
275
+
276
+ return MdnsDiscoverResponse(
277
+ success=True,
278
+ devices=device_responses,
279
+ )
280
+
281
+ except Exception as e:
282
+ return MdnsDiscoverResponse(
283
+ success=False,
284
+ devices=[],
285
+ error=str(e),
286
+ )
287
+
288
+
289
+ # QR Code Pairing Routes
290
+
291
+
292
+ @router.post("/api/devices/qr_pair/generate", response_model=QRPairGenerateResponse)
293
+ def generate_qr_pairing(timeout: int = 90) -> QRPairGenerateResponse:
294
+ """Generate QR code for wireless pairing and start mDNS listener.
295
+
296
+ Args:
297
+ timeout: Session timeout in seconds (default 90)
298
+
299
+ Returns:
300
+ QR code payload and session information
301
+ """
302
+ try:
303
+ from phone_agent.adb import ADBConnection
304
+
305
+ conn = ADBConnection()
306
+ session = qr_pairing_manager.create_session(
307
+ timeout=timeout, adb_path=conn.adb_path
308
+ )
309
+
310
+ return QRPairGenerateResponse(
311
+ success=True,
312
+ qr_payload=session.qr_payload,
313
+ session_id=session.session_id,
314
+ expires_at=session.expires_at,
315
+ message="QR code generated, listening for devices...",
316
+ )
317
+ except Exception as e:
318
+ return QRPairGenerateResponse(
319
+ success=False,
320
+ message=f"Failed to generate QR pairing: {str(e)}",
321
+ error="generation_failed",
322
+ )
323
+
324
+
325
+ def _get_status_message(status: str) -> str:
326
+ """Get user-friendly message for status code."""
327
+ messages = {
328
+ "listening": "等待手机扫描二维码...",
329
+ "pairing": "正在配对设备...",
330
+ "paired": "配对成功,正在连接...",
331
+ "connecting": "正在建立连接...",
332
+ "connected": "连接成功!",
333
+ "timeout": "超时:未检测到设备扫码",
334
+ "error": "配对失败",
335
+ }
336
+ return messages.get(status, "未知状态")
337
+
338
+
339
+ @router.get(
340
+ "/api/devices/qr_pair/status/{session_id}", response_model=QRPairStatusResponse
341
+ )
342
+ def get_qr_pairing_status(session_id: str) -> QRPairStatusResponse:
343
+ """Get current status of a QR pairing session.
344
+
345
+ Args:
346
+ session_id: Session UUID
347
+
348
+ Returns:
349
+ Current session status and device information if connected
350
+ """
351
+ session = qr_pairing_manager.get_session(session_id)
352
+
353
+ if not session:
354
+ return QRPairStatusResponse(
355
+ session_id=session_id,
356
+ status="error",
357
+ message="Session not found or expired",
358
+ error="session_not_found",
359
+ )
360
+
361
+ return QRPairStatusResponse(
362
+ session_id=session.session_id,
363
+ status=session.status,
364
+ device_id=session.device_id,
365
+ message=_get_status_message(session.status),
366
+ error=session.error_message,
367
+ )
368
+
369
+
370
+ @router.delete("/api/devices/qr_pair/{session_id}", response_model=QRPairCancelResponse)
371
+ def cancel_qr_pairing(session_id: str) -> QRPairCancelResponse:
372
+ """Cancel an active QR pairing session.
373
+
374
+ Args:
375
+ session_id: Session UUID to cancel
376
+
377
+ Returns:
378
+ Success status
379
+ """
380
+ success = qr_pairing_manager.cancel_session(session_id)
381
+
382
+ if success:
383
+ return QRPairCancelResponse(
384
+ success=True,
385
+ message="Pairing session cancelled",
386
+ )
387
+ else:
388
+ return QRPairCancelResponse(
389
+ success=False,
390
+ message="Session not found or already completed",
391
+ )
AutoGLM_GUI/schemas.py CHANGED
@@ -193,6 +193,24 @@ class WiFiManualConnectResponse(BaseModel):
193
193
  error: str | None = None
194
194
 
195
195
 
196
+ class WiFiPairRequest(BaseModel):
197
+ """WiFi pairing request (Android 11+ wireless debugging)."""
198
+
199
+ ip: str # Device IP address
200
+ pairing_port: int # Pairing port (from "Pair device with code" dialog)
201
+ pairing_code: str # 6-digit pairing code
202
+ connection_port: int = 5555 # Standard ADB connection port (default 5555)
203
+
204
+
205
+ class WiFiPairResponse(BaseModel):
206
+ """WiFi pairing response."""
207
+
208
+ success: bool
209
+ message: str
210
+ device_id: str | None = None # Device ID after connection (ip:connection_port)
211
+ error: str | None = None # Error code for frontend handling
212
+
213
+
196
214
  class VersionCheckResponse(BaseModel):
197
215
  """Version update check response."""
198
216
 
@@ -202,3 +220,55 @@ class VersionCheckResponse(BaseModel):
202
220
  release_url: str | None = None
203
221
  published_at: str | None = None
204
222
  error: str | None = None
223
+
224
+
225
+ class MdnsDeviceResponse(BaseModel):
226
+ """Single mDNS-discovered device."""
227
+
228
+ name: str # Device name (e.g., "adb-243a09b7-cbCO6P")
229
+ ip: str # IP address
230
+ port: int # Port number
231
+ has_pairing: bool # Whether pairing service was also advertised
232
+ service_type: str # Service type
233
+ pairing_port: int | None = None # Pairing port if has_pairing is True
234
+
235
+
236
+ class MdnsDiscoverResponse(BaseModel):
237
+ """mDNS device discovery response."""
238
+
239
+ success: bool
240
+ devices: list[MdnsDeviceResponse]
241
+ error: str | None = None
242
+
243
+
244
+ # QR Code Pairing Models
245
+
246
+
247
+ class QRPairGenerateResponse(BaseModel):
248
+ """QR code pairing generation response."""
249
+
250
+ success: bool
251
+ qr_payload: str | None = (
252
+ None # QR text payload (WIFI:T:ADB;S:{name};P:{password};;)
253
+ )
254
+ session_id: str | None = None # Session tracking ID (UUID)
255
+ expires_at: float | None = None # Unix timestamp when session expires
256
+ message: str
257
+ error: str | None = None # Error code for frontend handling
258
+
259
+
260
+ class QRPairStatusResponse(BaseModel):
261
+ """QR code pairing status response."""
262
+
263
+ session_id: str
264
+ status: str # "listening" | "pairing" | "paired" | "connecting" | "connected" | "timeout" | "error"
265
+ device_id: str | None = None # Device ID when connected (ip:port)
266
+ message: str
267
+ error: str | None = None # Error details
268
+
269
+
270
+ class QRPairCancelResponse(BaseModel):
271
+ """QR code pairing cancellation response."""
272
+
273
+ success: bool
274
+ message: str
@@ -1 +1 @@
1
- import{j as o}from"./index-DH-Dl4tK.js";function t(){return o.jsx("div",{className:"p-2",children:o.jsx("h3",{children:"About"})})}export{t as component};
1
+ import{j as o}from"./index-C1k5Ch1V.js";function t(){return o.jsx("div",{className:"p-2",children:o.jsx("h3",{children:"About"})})}export{t as component};