diff --git a/server.log b/server.log index 22ca4dc..951c102 100644 --- a/server.log +++ b/server.log @@ -1,98 +1,549 @@ -INFO: Started server process [70935] +INFO: Started server process [71515] INFO: Waiting for application startup. -2026-02-20 20:04:15,011 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json -2026-02-20 20:04:15,011 - __main__ - INFO - Auto-starting fan control (enabled in config) -2026-02-20 20:04:15,162 - fan_controller - INFO - Connected to IPMI at 192.168.5.191 -2026-02-20 20:04:15,162 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888 -2026-02-20 20:04:15,163 - fan_controller - INFO - IPMI Controller service started +2026-02-20 20:06:54,388 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json +2026-02-20 20:06:54,388 - __main__ - INFO - Auto-starting fan control (enabled in config) +2026-02-20 20:06:54,546 - fan_controller - INFO - Connected to IPMI at 192.168.5.191 +2026-02-20 20:06:54,546 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888 +2026-02-20 20:06:54,547 - fan_controller - INFO - IPMI Controller service started INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) -2026-02-20 20:04:15,314 - fan_controller - INFO - Manual fan control enabled -INFO: 192.168.5.30:62669 - "GET /api/status HTTP/1.1" 401 Unauthorized -INFO: 192.168.5.30:62669 - "GET /login HTTP/1.1" 200 OK -INFO: 192.168.5.30:62669 - "GET /icons/favicon.svg HTTP/1.1" 304 Not Modified -2026-02-20 20:04:20,942 - fan_controller - INFO - Fan 0xff speed set to 27% -2026-02-20 20:04:20,942 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) -2026-02-20 20:04:36,853 - fan_controller - INFO - Fan 0xff speed set to 31% -2026-02-20 20:04:36,853 - fan_controller - INFO - All fans set to 31% (Temp 38.0°C) -INFO: 127.0.0.1:34870 - "GET /api/status HTTP/1.1" 401 Unauthorized -2026-02-20 20:04:51,781 - fan_controller - INFO - Fan 0xff speed set to 27% -2026-02-20 20:04:51,781 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) -2026-02-20 20:05:07,261 - fan_controller - INFO - Fan 0xff speed set to 25% -2026-02-20 20:05:07,261 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) -INFO: 192.168.5.30:65404 - "GET /login HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /favicon.ico HTTP/1.1" 200 OK +INFO: 192.168.5.30:61891 - "GET /api/status HTTP/1.1" 401 Unauthorized +2026-02-20 20:06:54,707 - fan_controller - INFO - Manual fan control enabled +INFO: 192.168.5.30:61891 - "GET /login HTTP/1.1" 200 OK +INFO: 192.168.5.30:61891 - "GET /icons/favicon.svg HTTP/1.1" 304 Not Modified +2026-02-20 20:06:59,911 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:06:59,911 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) /home/devmatrix/projects/fan-controller-v2/web_server.py:153: 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: 192.168.5.30:65404 - "POST /api/auth/login HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET / HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /icons/sun.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:52052 - "POST /api/auth/login HTTP/1.1" 200 OK +INFO: 192.168.5.30:52052 - "GET / HTTP/1.1" 200 OK +INFO: 192.168.5.30:52052 - "GET /icons/auto-mode.svg HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:58005 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:64268 - "GET /icons/lock-closed.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:63723 - "GET /icons/auto-mode.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:65404 - "GET /icons/thermometer.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:64382 - "GET /icons/server-stack.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:50712 - "GET /icons/arrow-right-on-rectangle.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:58005 - "GET /icons/list-bullet.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:63723 - "GET /icons/chart-bar.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:64268 - "GET /icons/adjustments-horizontal.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:65404 - "GET /icons/document-text.svg HTTP/1.1" 304 Not Modified -INFO: 192.168.5.30:65404 - "GET /favicon.ico HTTP/1.1" 200 OK -2026-02-20 20:05:22,464 - fan_controller - INFO - Fan 0xff speed set to 27% -2026-02-20 20:05:22,464 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +INFO: 192.168.5.30:55325 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /icons/thermometer.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:63188 - "GET /favicon.ico HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:07:15,453 - fan_controller - INFO - Fan 0xff speed set to 25% +2026-02-20 20:07:15,453 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 127.0.0.1:50858 - "GET /icons/auto-mode.svg HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -2026-02-20 20:05:53,251 - fan_controller - INFO - Fan 0xff speed set to 29% -2026-02-20 20:05:53,251 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:07:30,915 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:07:30,915 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -2026-02-20 20:06:08,235 - fan_controller - INFO - Fan 0xff speed set to 25% -2026-02-20 20:06:08,235 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:07:45,964 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:07:45,965 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -2026-02-20 20:06:23,620 - fan_controller - INFO - Fan 0xff speed set to 29% -2026-02-20 20:06:23,620 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK /home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK -INFO: 192.168.5.30:65404 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:63188 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:09:03,867 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:09:03,867 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:09:19,798 - fan_controller - INFO - Fan 0xff speed set to 31% +2026-02-20 20:09:19,799 - fan_controller - INFO - All fans set to 31% (Temp 38.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:55296 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:09:35,346 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:09:35,347 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:09:50,823 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:09:50,823 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:10:06,006 - fan_controller - INFO - Fan 0xff speed set to 31% +2026-02-20 20:10:06,006 - fan_controller - INFO - All fans set to 31% (Temp 38.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:57942 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:10:54,206 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:10:54,206 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:64073 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:13:15,599 - fan_controller - INFO - Fan 0xff speed set to 25% +2026-02-20 20:13:15,600 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:13:32,024 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:13:32,025 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:13:48,155 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:13:48,156 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:14:04,773 - fan_controller - INFO - Fan 0xff speed set to 25% +2026-02-20 20:14:04,774 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:14:20,306 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:14:20,307 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:14:36,209 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:14:36,209 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:14:51,615 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:14:51,616 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:15:22,579 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:15:22,579 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:15:37,470 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:15:37,470 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET / HTTP/1.1" 200 OK +INFO: 192.168.5.30:50577 - "GET /icons/favicon.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:50577 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:60159 - "GET /icons/lock-closed.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:57390 - "GET /icons/auto-mode.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:59075 - "GET /icons/sun.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:55085 - "GET /icons/server-stack.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:51598 - "GET /icons/arrow-right-on-rectangle.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:50577 - "GET /icons/thermometer.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:57390 - "GET /icons/adjustments-horizontal.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:60159 - "GET /icons/list-bullet.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:51598 - "GET /icons/chart-bar.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:59075 - "GET /icons/document-text.svg HTTP/1.1" 304 Not Modified +INFO: 192.168.5.30:59075 - "GET /favicon.ico HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:15:52,508 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:15:52,508 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:16:39,127 - fan_controller - INFO - Fan 0xff speed set to 25% +2026-02-20 20:16:39,127 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:16:55,091 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:16:55,092 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:17:11,182 - fan_controller - INFO - Fan 0xff speed set to 25% +2026-02-20 20:17:11,182 - fan_controller - INFO - All fans set to 25% (Temp 35.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:17:26,052 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:17:26,052 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:17:41,039 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:17:41,040 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:18:13,111 - fan_controller - INFO - Fan 0xff speed set to 31% +2026-02-20 20:18:13,111 - fan_controller - INFO - All fans set to 31% (Temp 38.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:18:28,096 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:18:28,097 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:18:44,743 - fan_controller - INFO - Fan 0xff speed set to 29% +2026-02-20 20:18:44,743 - fan_controller - INFO - All fans set to 29% (Temp 37.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +2026-02-20 20:19:00,200 - fan_controller - INFO - Fan 0xff speed set to 27% +2026-02-20 20:19:00,201 - fan_controller - INFO - All fans set to 27% (Temp 36.0°C) +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +/home/devmatrix/projects/fan-controller-v2/web_server.py:160: 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: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK +INFO: 192.168.5.30:59075 - "GET /api/status HTTP/1.1" 200 OK diff --git a/web_server.py b/web_server.py index a7282cc..d478ca4 100644 --- a/web_server.py +++ b/web_server.py @@ -102,7 +102,8 @@ class FanGroupCreate(BaseModel): class ManualSpeedRequest(BaseModel): speed: int = Field(..., ge=0, le=100) - fan_id: str = "0xff" + fan_id: Optional[str] = "0xff" + group: Optional[str] = None class IdentifyRequest(BaseModel): fan_id: str @@ -554,7 +555,7 @@ def get_html(theme="dark"): - + @@ -646,15 +647,21 @@ def get_html(theme="dark"):
-

Fan Configuration

+

Fan Groups Management

+
+

Loading fan groups...

+
+

Individual Fan Configuration

Connect to IPMI to see fans

-

Fan Groups

-
-

No groups defined

+ +

Quick Actions

+
+ + +
-
@@ -697,6 +704,44 @@ def get_html(theme="dark"):
+ + + + + + + + ''' @@ -1320,6 +1537,20 @@ async def api_control_manual(req: ManualSpeedRequest, username: str = Depends(ge if not service._init_controller(): return {"success": False, "error": "Not connected"} + # Handle group speed setting + if req.group: + groups = service.config.get('fan_groups', {}) + if req.group not in groups: + return {"success": False, "error": f"Group '{req.group}' not found"} + + fan_ids = groups[req.group].get('fan_ids', []) + success = True + for fan_id in fan_ids: + if not service.set_manual_speed(req.speed, fan_id): + success = False + return {"success": success} + + # Handle individual fan or all fans if service.set_manual_speed(req.speed, req.fan_id): return {"success": True} return {"success": False, "error": "Failed"} @@ -1419,6 +1650,34 @@ async def api_stop_identify(username: str = Depends(get_current_user)): service.stop_identify() return {"success": True} +# Fan Groups API +@app.post("/api/fans/groups") +async def api_save_group(data: dict, username: str = Depends(get_current_user)): + service = get_service(str(CONFIG_FILE)) + name = data.get('name') + fan_ids = data.get('fan_ids', []) + + if not name: + return {"success": False, "error": "Group name required"} + + if 'fan_groups' not in service.config: + service.config['fan_groups'] = {} + + service.config['fan_groups'][name] = {'fan_ids': fan_ids} + service._save_config() + return {"success": True} + +@app.delete("/api/fans/groups") +async def api_delete_group(data: dict, username: str = Depends(get_current_user)): + service = get_service(str(CONFIG_FILE)) + name = data.get('name') + + if 'fan_groups' in service.config and name in service.config['fan_groups']: + del service.config['fan_groups'][name] + service._save_config() + return {"success": True} + return {"success": False, "error": "Group not found"} + # Public API (no auth required - for external integrations) @app.get("/api/public/status") async def api_public_status():