#!/usr/bin/env python3 """第16轮诊断:IoT&A 自动化规则引擎 + 控件CDP 测试脚本""" import json, urllib.request, urllib.error, sys, time, subprocess GATEWAY = "http://localhost:8080" CDP = "http://127.0.0.1:9225" def req(method, path, body=None, headers=None): url = f"{GATEWAY}{path}" if headers is None: headers = {} if body is not None: data = json.dumps(body).encode() headers["Content-Type"] = "application/json" else: data = None try: r = urllib.request.Request(url, data=data, headers=headers, method=method) resp = urllib.request.urlopen(r, timeout=10) return json.loads(resp.read()) except urllib.error.HTTPError as e: body = e.read().decode() try: return json.loads(body) except: return {"error": body, "status": e.code} except Exception as e: return {"error": str(e)} def get_token(): result = req("POST", "/api/v1/auth/login", {"username": "yeij0942", "password": "Jiang1143218570"}) return result.get("token", "") print("=" * 60) print("1. JWT Token 获取") print("=" * 60) token = get_token() if token: print(f"✅ Token 获取成功: {token[:40]}...") else: print("❌ Token 获取失败") sys.exit(1) auth_headers = {"Authorization": f"Bearer {token}"} print("\n" + "=" * 60) print("2. Automation 规则引擎 API CRUD 测试") print("=" * 60) # 2a. GET Rules (should be empty) print("\n2a. GET /api/v1/automation/rules") result = req("GET", "/api/v1/automation/rules", headers=auth_headers) count = result.get("count", -1) print(f" count={count} {'✅' if count is not None else '⚠️'}") # 2b. POST Create Rule print("\n2b. POST /api/v1/automation/rules (创建规则)") create_body = { "name": "测试规则-关灯", "description": "每天晚上22点关闭客厅灯", "trigger_type": "schedule", "trigger_config": {"time": "22:00", "days": ["mon","tue","wed","thu","fri"]}, "actions": [{"type": "set_device", "device_id": "light-livingroom", "property": "status", "value": "off"}] } result = req("POST", "/api/v1/automation/rules", body=create_body, headers=auth_headers) rule_id = result.get("rule", {}).get("id", "") print(f" success={result.get('success')} rule_id={rule_id[:16] if rule_id else 'N/A'}...") if not result.get("success"): print(f" ERROR: {json.dumps(result, indent=2, ensure_ascii=False)[:500]}") # 2c. GET Rules after create if rule_id: print("\n2c. GET /api/v1/automation/rules") result = req("GET", "/api/v1/automation/rules", headers=auth_headers) name0 = result.get("rules", [{}])[0].get("name", "N/A") if result.get("rules") else "N/A" print(f" count={result.get('count')} first_name={name0}") # 2d. GET single print(f"\n2d. GET /api/v1/automation/rules/{rule_id}") result = req("GET", f"/api/v1/automation/rules/{rule_id}", headers=auth_headers) r = result.get("rule", {}) print(f" name={r.get('name')} trigger_type={r.get('trigger_type')} enabled={r.get('enabled')}") # 2e. PUT Update print(f"\n2e. PUT /api/v1/automation/rules/{rule_id}") result = req("PUT", f"/api/v1/automation/rules/{rule_id}", body={"name": "测试规则-已更新", "enabled": False}, headers=auth_headers) r = result.get("rule", {}) print(f" success={result.get('success')} name={r.get('name')} enabled={r.get('enabled')}") # 2f. DELETE print(f"\n2f. DELETE /api/v1/automation/rules/{rule_id}") result = req("DELETE", f"/api/v1/automation/rules/{rule_id}", headers=auth_headers) print(f" success={result.get('success')}") # 2g. Verify deleted print("\n2g. GET /api/v1/automation/rules (验证删除)") result = req("GET", "/api/v1/automation/rules", headers=auth_headers) print(f" count={result.get('count')} {'✅' if result.get('count') == 0 else '⚠️'}") # 2h. Unauthenticated print("\n2h. GET /api/v1/automation/rules (未认证)") result = req("GET", "/api/v1/automation/rules") print(f" error={result.get('error','N/A')[:60]}") # 2i. Manual trigger print("\n2i. POST /api/v1/automation/rules/:id/trigger (手动触发)") cbody = {"name": "触发测试", "trigger_type": "manual", "actions": [{"type": "notify", "title": "测试", "body": "通知"}]} result = req("POST", "/api/v1/automation/rules", body=cbody, headers=auth_headers) trid = result.get("rule", {}).get("id", "") if trid: result = req("POST", f"/api/v1/automation/rules/{trid}/trigger", headers=auth_headers) print(f" success={result.get('success')} msg={result.get('message','')}") req("DELETE", f"/api/v1/automation/rules/{trid}", headers=auth_headers) else: print(" ❌ 创建失败") # 2j. Scenes print("\n2j. GET /api/v1/automation/scenes") result = req("GET", "/api/v1/automation/scenes", headers=auth_headers) print(f" count={result.get('count')}") print("\n" + "=" * 60) print("3. IoT 调试服务测试") print("=" * 60) res = json.loads(urllib.request.urlopen("http://localhost:8083/api/v1/devices").read()) print(f"3a. 设备总数: {res.get('total', 0)}") for d in res.get("devices", []): print(f" {d['name']} ({d['type']}): {d.get('status','')}") print("\n3b. Toggle light-bedroom") r = urllib.request.Request("http://localhost:8083/api/v1/devices/light-bedroom/toggle", method="POST") result = json.loads(urllib.request.urlopen(r).read()) dev = result.get("device", {}) print(f" action={result.get('action')} {dev.get('name')} status={dev.get('status')}") print("\n3c. Set temperature (ac-livingroom)") r = urllib.request.Request("http://localhost:8083/api/v1/devices/ac-livingroom/set", data=json.dumps({"field": "temperature", "value": 24}).encode(), headers={"Content-Type": "application/json"}, method="POST") result = json.loads(urllib.request.urlopen(r).read()) dev = result.get("device", {}) print(f" {dev.get('name')} temperature={dev.get('temperature')}°C") print("\n3d. History") result = json.loads(urllib.request.urlopen("http://localhost:8083/api/v1/devices/light-bedroom/history").read()) print(f" device_id={result.get('device_id')} history={len(result.get('history',[]))} entries") print("\n" + "=" * 60) print("4. CDP 前端 IoT 控件验证") print("=" * 60) pages = json.loads(urllib.request.urlopen(f"{CDP}/json").read()) target = None for p in pages: if "localhost:5199" in p.get("url", ""): target = p break if target: ws_url = target.get("webSocketDebuggerUrl", "") print(f"4a. 目标页面: {target.get('title','')[:60]}") print(f"4b. WebSocket: {ws_url[:80]}...") # Use websockets to execute JS print("\n4c. CDP Runtime.evaluate 检查 IoTStatusBar") try: import asyncio try: import websockets except ImportError: print(" ⚠️ websockets not installed, trying pip install...") subprocess.run([sys.executable, "-m", "pip", "install", "websockets", "-q"], timeout=30) import websockets async def cdp_eval(): async with websockets.connect(ws_url, max_size=2**24) as ws: # Runtime.enable await ws.send(json.dumps({"id": 1, "method": "Runtime.enable"})) await asyncio.wait_for(ws.recv(), timeout=5) # Evaluate JS js_code = """ (function() { var r = {}; var allDivs = document.querySelectorAll('div'); r.totalDivs = allDivs.length; r.iotTexts = []; for (var i = 0; i < allDivs.length; i++) { var t = allDivs[i].textContent || ''; if (t.indexOf('IoT') !== -1 || t.indexOf('iot') !== -1) { r.iotTexts.push(t.substring(0, 100)); if (r.iotTexts.length >= 10) break; } } r.hasRoot = !!document.getElementById('root'); r.title = document.title; r.bodyText = (document.body ? document.body.innerText : '').substring(0, 300); return JSON.stringify(r); })() """ await ws.send(json.dumps({"id": 2, "method": "Runtime.evaluate", "params": {"expression": js_code, "returnByValue": True}})) resp = await asyncio.wait_for(ws.recv(), timeout=10) data = json.loads(resp) result_val = data.get("result", {}).get("result", {}).get("value", "N/A") return result_val result = asyncio.new_event_loop().run_until_complete(cdp_eval()) parsed = json.loads(result) if result and result != "N/A" else {} print(f" title={parsed.get('title','')}") print(f" hasRoot={parsed.get('hasRoot')} totalDivs={parsed.get('totalDivs')}") print(f" iotTexts={parsed.get('iotTexts',[])}") print(f" bodyText(first 300): {parsed.get('bodyText','')}") except Exception as e: print(f" ❌ CDP 错误: {e}") else: print("4a. ❌ 未找到 localhost:5199 页面") for p in pages: print(f" {p.get('url','?')[:100]}") print("\n" + "=" * 60) print("5. tool-engine IoT 工具执行测试") print("=" * 60) # 5a. iot_query print("\n5a. tool-engine iot_query") r = urllib.request.Request("http://localhost:8092/api/v1/tools/execute", data=json.dumps({"tool": "iot_query", "arguments": {}}).encode(), headers={"Content-Type": "application/json"}, method="POST") try: result = json.loads(urllib.request.urlopen(r, timeout=10).read()) print(f" output: {result.get('output','')[:200]}") if result.get('error'): print(f" error: {result['error'][:200]}") except Exception as e: print(f" ❌ {e}") # 5b. iot_control toggle print("\n5b. tool-engine iot_control (toggle ac-livingroom)") r = urllib.request.Request("http://localhost:8092/api/v1/tools/execute", data=json.dumps({"tool": "iot_control", "arguments": {"device_id": "ac-livingroom", "action": "toggle"}}).encode(), headers={"Content-Type": "application/json"}, method="POST") try: result = json.loads(urllib.request.urlopen(r, timeout=10).read()) print(f" output: {result.get('output','')[:200]}") if result.get('error'): print(f" error: {result['error'][:200]}") except Exception as e: print(f" ❌ {e}") # 5c. iot_control set_temperature print("\n5c. tool-engine iot_control (set_temperature 28)") r = urllib.request.Request("http://localhost:8092/api/v1/tools/execute", data=json.dumps({"tool": "iot_control", "arguments": {"device_id": "ac-livingroom", "action": "set_temperature", "value": 28}}).encode(), headers={"Content-Type": "application/json"}, method="POST") try: result = json.loads(urllib.request.urlopen(r, timeout=10).read()) print(f" output: {result.get('output','')[:200]}") if result.get('error'): print(f" error: {result['error'][:200]}") except Exception as e: print(f" ❌ {e}") print("\n" + "=" * 60) print("诊断测试完成") print("=" * 60)