From a100327769eb71c8a98156ef6b5bab266eae896a Mon Sep 17 00:00:00 2001 From: devmatrix Date: Fri, 20 Feb 2026 17:37:31 +0000 Subject: [PATCH] Add 3-step onboarding wizard with IPMI/HTTP test buttons + auto-activate fan curve --- __pycache__/fan_controller.cpython-312.pyc | Bin 36174 -> 36705 bytes __pycache__/web_server.cpython-312.pyc | Bin 0 -> 64816 bytes data/config.json | 2 +- data/users.json | 1 - fan_controller.py | 52 +++++- server.log | 29 +--- web_server.py | 183 ++++++--------------- 7 files changed, 99 insertions(+), 168 deletions(-) create mode 100644 __pycache__/web_server.cpython-312.pyc delete mode 100644 data/users.json diff --git a/__pycache__/fan_controller.cpython-312.pyc b/__pycache__/fan_controller.cpython-312.pyc index ea1e65d63263b99dd9b4eb2acd81678e40abccb3..a8884b9e71825a7777848d973df0ea3875b3a01c 100644 GIT binary patch delta 2555 zcmZuyX>3$g6n^*3(q;PE={{|zou!?P7HLaBEVa@lWoala1%gr>hQ4Vnoz9Ru)5a9q zQ49z|BJl!F1eN$hAQA&U6ayhb2pWWF>XMZCfk-rlKl%`%E@(XGzG-NTlgxL{x%a!@ zJ$F0ri#NpY-x9O0Nm8Z&zx9U`gO1qg?5m4Rc@XO0fA1YpxxHusiJRn#Bcg1R?c6$K zC%2Vy6}K+AoLjf-;MO9$x%J3iZoP6ow?26ex7Bhk+5wYXD%Z$!kLY0hW4y;9Ycsi0 zGq~k>*dCWHFgd2+kzL$+Q_{Ya^o(T2EJ-annVDoJlD zNmR_yvaE$n>*V?)Mz*e@kIYxjHB^y!QXd$JMUv?)flwga7nJW*z{Z^r=}It3Lv(8p znkvD+lZD^~pf}K)i%@`2kMKA`GeSE;2ZA4=3xL)D_;l2Vel-G)rn|BAP6?up{sty# z>=_IN!?C2PD@gkzbQm*A>g50(@)_CUMwgTo=!*@G2K{{_bTp{=8%v0IOk`tE`r~U! zaptY8;zXWmKvy*ik~OhfP2)kj7G&@sQ;;l)eAUDQvmiN-E>LrMkS9nL399Dvpa6U7 z)j}SW3R2Pj5w(m5R!yy(2NfDEZsU<1wewU551iUWl{}~tq>_YFb@9M0NTrGSs)q+& zL9!jyt3Dpg(Nd`9L9HN_CxU7n59$S{7gkkRJXLB1TkcWQ1!vXq&6Ae8DMux{+~i=_ zn=J4*+xsRh^=fgJtw=2u+|^S~&t03jJWq8aKQL*jWfz;OfLLCXp9!3z6sIh^EKhX; zhn;WoVR!X^y6t&)TS;wMw%SjGBI~b%BIdPQzt5zGMJ4yC9+8x=$#x4HZ=O#|+398v zIjY=f{+-k~2^_J3F2F-~gHJsGNiviqqfrAi+VMy6;!!7zv$B#l6FI54+764kHssrp zua`%Lw^E$KW~{{4D)yj#89AdowREFMYS^30H;^mrkLBa`C$QIwat+``u@u9|1ajMvCdu5`={;t78FaWGx-7{Pdz+*B@gzbY=tLH%Y9$ZA1& zq}_rYg#bz8n&)URASWe%UnCr(k&r(WiEO22wttPCbg~%r*i+ioMV(iLCC_u5g)3FA2D&%4BZpkN` z*|{z4?IxsVB4BCJYR`T2uzb-62s-S&ie3hKF7T2DURsiAZNdJ)NGQg-hDr(_oy)9U zVG~1j#K%qz^^o;UABr0{qQ*4a6LOFqSWyk>Ql>)P2J$ykiwjs2ogfF<1?nTy>?gXp zVj3okqMUu^r`^v0sEpOXT8i03w2a}O+z~PTH^r=L&95w@StO|3;kHV?+2f}{- zA$T7)JsM>%j+QHDo3gGxM=TvX2kNM_BxPTD!6q%WKDyRN+eWb^0D^BYH*|IyMO2JRBA!sn4R`|-X; z1O4zViiG?E3C2 za~OEOg*1-(3?YWV?*LzMS|~CwFc==7h3x#E)8+#>34ZHoDZ(MP{gr-)wpq}_pL1<3 lzkYwFHx5VSkx;OS9%s6})6$YOQgBy5RF`EdY5SIve*t)=l;r>b delta 2053 zcmZvde{54#6vuns+HPZeTeo%X)^%%F_M`2lVPM0dwZ-tac-+sMhkt(IC@w5SuswL;Rj#td{flD5lR{^t`5gjMfTl{_A zSRi`23|s75#9@K8(}*q-_WPyFPSlr+2@Ql+o-w~HBdL|V>?N0ucg?Sqx6UsD;Rx^p z3q0|9^R$cE=0v`tL#607qBZe^l1@4!r87xS(}+ch0ws&IK_fc$H!69g^EF~wf++>0 zjnv5E>EM;ROZ6}}Je5ITkj?@CvNmppZ;)Fw~BweKuEs2>* zHEE|twC&d_F4FGQ!ZoC4XvETlU#TbEpm8`ci$e2+#fll^SuXPMq8fg0k`dVR@HD5^+OL8y_ll8h6aPo+DEKBJFxo&Z%03yG-q=@fro(VbpY;!fNmv1-! z0r@7-Xk0qhh?Qj))U1R>}*S304Q-(6V) z@5%0t4q=K#9a#u`duJ8Q;-3et{7k0-K9MhU?hqEGk^e@*VzO!?Jn%h>1~L&zeb)=j z@0F6G#}^7lSt#HMghFAaOu<&rWGvia`br(prV+Upc9fn4nQ zGOgtwGNJ2)1ypa9)*Fk4u+c#SkI^($4U$RblRvoGk{V4(04q; zO}pm;mY?!nN1oad5dk~w_WQet9Yb^ZuBe$Og9d&)P=Mim6KIX!L!)H2*B2dF>-YF# zY^^_;(c}$ygFe5+T(s&<0$qA`i*ScOwc2~}n zgr*jYzcn~RUy8vcO?+p}#J?Y$1MS=#jO)87k4ZirEQdDCxE5;VE5QyO{K*xwk#|Hl zz#9Hl)CH6Lujup0o|swx6lGb9S=K^8J`}43n3T`0eG>F)4RyS3ovmn+LJOne{bPQ= zK;5`mRx#p_dc1*vr_URVye98hcMRq-iX(zp&Z0<{Hgyxr$;|^_T5Q_!CsplLXgE}% zR(sEo2&3}Bp<_^}29b?J>|>hTLh4bzW_>&C;$N))46^y~a2p(uPYjnpmTIgXaW}uR z!Ga0u<165x+#TNyS*d=S5B%1~)o?^!v1vkxtEco5`c7uz@elfaQQX6Aw3fQ6jwa|4 z$?Pa@il2F-G3Jk?Hj`Z9E~)1_b=t{Vy}D{B!{p|Fe5Eb{rOzOPAKFr?r?ZDo&8aOd zczbhSw+Tk|HPQt_UMZ5)(W4Z>SE*^M12*AHU^Vn((IehIJbs~I-$4IqzH95Acn*27 z5!469O=>n_BQ2#q1?sh5NPSI&1q2<9<`P<|*+wX#rk+qmphLpC3A9b@Wx^|jR|&&} z&43B9P5k|l+T144)$ebx7N*vCz8+!mkJgYlY%s4Nm? I%bRWRFMvW9kpKVy diff --git a/__pycache__/web_server.cpython-312.pyc b/__pycache__/web_server.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8132f71477f24709fdc29c2a9c78e423c6341bc GIT binary patch literal 64816 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 O_SSjk>wLY {"name": "Custom Name", "group": "group1"} "fan_groups": {}, # group_name -> {"fans": ["0x00", "0x01"], "curve": "Default"} # Fan Curves + "active_curve": "Balanced", "fan_curves": { - "Default": { + "Balanced": { "points": [ - {"temp": 30, "speed": 15}, - {"temp": 40, "speed": 25}, - {"temp": 50, "speed": 40}, - {"temp": 60, "speed": 60}, - {"temp": 70, "speed": 80}, - {"temp": 80, "speed": 100}, + {"temp": 30, "speed": 10}, + {"temp": 35, "speed": 12}, + {"temp": 40, "speed": 15}, + {"temp": 45, "speed": 20}, + {"temp": 50, "speed": 30}, + {"temp": 55, "speed": 40}, + {"temp": 60, "speed": 55}, + {"temp": 65, "speed": 70}, + {"temp": 70, "speed": 85}, + {"temp": 75, "speed": 95}, + {"temp": 80, "speed": 100} + ], + "sensor_source": "cpu", + "applies_to": "all" + }, + "Silent": { + "points": [ + {"temp": 30, "speed": 5}, + {"temp": 40, "speed": 10}, + {"temp": 50, "speed": 15}, + {"temp": 55, "speed": 25}, + {"temp": 60, "speed": 35}, + {"temp": 65, "speed": 50}, + {"temp": 70, "speed": 70}, + {"temp": 75, "speed": 85}, + {"temp": 80, "speed": 100} + ], + "sensor_source": "cpu", + "applies_to": "all" + }, + "Performance": { + "points": [ + {"temp": 30, "speed": 20}, + {"temp": 35, "speed": 25}, + {"temp": 40, "speed": 35}, + {"temp": 45, "speed": 45}, + {"temp": 50, "speed": 55}, + {"temp": 55, "speed": 70}, + {"temp": 60, "speed": 85}, + {"temp": 65, "speed": 95}, + {"temp": 70, "speed": 100} ], "sensor_source": "cpu", "applies_to": "all" diff --git a/server.log b/server.log index 1b4e7c8..ebd2c40 100644 --- a/server.log +++ b/server.log @@ -1,26 +1,9 @@ -INFO: Started server process [29754] +INFO: Started server process [33302] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) -INFO: 192.168.5.30:55306 - "GET /api/status HTTP/1.1" 401 Unauthorized -INFO: 192.168.5.30:55306 - "GET /login HTTP/1.1" 200 OK -INFO: 192.168.5.30:54183 - "GET /login HTTP/1.1" 200 OK -/home/devmatrix/projects/fan-controller-v2/web_server.py:149: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC). - self._sessions[token] = (username, datetime.utcnow() + timedelta(days=7)) -INFO: 127.0.0.1:36770 - "POST /api/auth/login HTTP/1.1" 200 OK -/home/devmatrix/projects/fan-controller-v2/web_server.py:156: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC). - if datetime.utcnow() > expiry: -2026-02-20 17:23:59,479 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json -2026-02-20 17:23:59,481 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json -2026-02-20 17:23:59,638 - fan_controller - INFO - Connected to IPMI at 192.168.5.191 -2026-02-20 17:23:59,638 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888 -2026-02-20 17:23:59,639 - fan_controller - INFO - IPMI Controller service started -INFO: 127.0.0.1:36774 - "POST /api/control/auto HTTP/1.1" 200 OK -2026-02-20 17:23:59,796 - fan_controller - INFO - Manual fan control enabled -2026-02-20 17:24:04,639 - fan_controller - INFO - Fan 0xff speed set to 12% -2026-02-20 17:24:04,640 - fan_controller - INFO - All fans set to 12% (Temp 35.0°C) -/home/devmatrix/projects/fan-controller-v2/web_server.py:156: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC). - if datetime.utcnow() > expiry: -INFO: 127.0.0.1:34428 - "GET /api/status HTTP/1.1" 200 OK -2026-02-20 17:24:19,530 - fan_controller - INFO - Fan 0xff speed set to 13% -2026-02-20 17:24:19,531 - fan_controller - INFO - All fans set to 13% (Temp 37.0°C) +INFO: 127.0.0.1:44708 - "GET / HTTP/1.1" 200 OK +INFO: 192.168.5.30:64440 - "GET /login HTTP/1.1" 200 OK +INFO: 192.168.5.30:56562 - "GET /favicon.ico HTTP/1.1" 200 OK +INFO: 192.168.5.30:49401 - "POST /api/setup/test-ipmi HTTP/1.1" 200 OK +INFO: 192.168.5.30:49401 - "GET /favicon.ico HTTP/1.1" 200 OK diff --git a/web_server.py b/web_server.py index b029d1d..1103f9c 100644 --- a/web_server.py +++ b/web_server.py @@ -49,6 +49,9 @@ class SetupRequest(BaseModel): ipmi_username: str ipmi_password: str ipmi_port: int = 623 + http_sensor_enabled: Optional[bool] = False + http_sensor_url: Optional[str] = None + http_sensor_timeout: Optional[int] = 10 class IPMIConfig(BaseModel): host: str @@ -1142,135 +1145,8 @@ LOGIN_HTML = ''' ''' -SETUP_HTML = ''' - - - - - - Setup - IPMI Controller - - - -
-

🌡️ IPMI Controller

-

Initial Setup

-
-
-
-

👤 Admin Account

-
-
-
-
-
-
-

🖥️ IPMI Connection

-
-
-
-
-
-
-
- -
-
- - -''' +with open('/home/devmatrix/.openclaw/workspace/setup_html.txt', 'r') as f: + SETUP_HTML = f.read().strip().replace("SETUP_HTML = '''", "").replace("'''", "") # FastAPI app @asynccontextmanager @@ -1344,6 +1220,35 @@ async def api_change_password(data: ChangePassword, username: str = Depends(get_ return {"success": False, "error": "Current password is incorrect"} return {"success": True} +# Setup test endpoints (no auth required for setup) +@app.post("/api/setup/test-ipmi") +async def api_setup_test_ipmi(data: dict): + """Test IPMI connection during setup.""" + from fan_controller import IPMIFanController + try: + controller = IPMIFanController( + host=data.get('host'), + username=data.get('username'), + password=data.get('password'), + port=data.get('port', 623) + ) + if controller.test_connection(): + return {"success": True} + return {"success": False, "error": "Connection failed"} + except Exception as e: + return {"success": False, "error": str(e)} + +@app.post("/api/setup/test-http") +async def api_setup_test_http(data: dict): + """Test HTTP sensor during setup.""" + from fan_controller import HTTPSensorClient + try: + client = HTTPSensorClient(url=data.get('url'), timeout=10) + temps = client.fetch_sensors() + return {"success": True, "sensors": len(temps)} + except Exception as e: + return {"success": False, "error": str(e)} + @app.post("/api/setup") async def api_setup(data: SetupRequest): if user_manager.is_setup_complete(): @@ -1353,12 +1258,20 @@ async def api_setup(data: SetupRequest): return {"success": False, "error": "Failed to create user"} service = get_service(str(CONFIG_FILE)) - service.update_config( - ipmi_host=data.ipmi_host, - ipmi_username=data.ipmi_username, - ipmi_password=data.ipmi_password, - ipmi_port=data.ipmi_port - ) + updates = { + "ipmi_host": data.ipmi_host, + "ipmi_username": data.ipmi_username, + "ipmi_password": data.ipmi_password, + "ipmi_port": data.ipmi_port + } + + # Add HTTP config if provided + if hasattr(data, 'http_sensor_enabled') and data.http_sensor_enabled: + updates["http_sensor_enabled"] = True + updates["http_sensor_url"] = getattr(data, 'http_sensor_url', '') + updates["http_sensor_timeout"] = getattr(data, 'http_sensor_timeout', 10) + + service.update_config(**updates) token = user_manager.create_token(data.admin_username) return {"success": True, "token": token}