Add fan icon as app logo and browser favicon

This commit is contained in:
devmatrix 2026-02-20 19:45:13 +00:00
parent 49b5878940
commit bd0ba4a53d
4 changed files with 102 additions and 20 deletions

View File

@ -1,7 +1,98 @@
INFO: Started server process [64283]
INFO: Started server process [64990]
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: 127.0.0.1:39150 - "GET /icons/wifi.svg HTTP/1.1" 200 OK
INFO: 127.0.0.1:39164 - "GET / HTTP/1.1" 200 OK
INFO: 127.0.0.1:41300 - "GET / HTTP/1.1" 200 OK
INFO: 192.168.5.30:56691 - "GET /favicon.ico HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "POST /api/setup/test-ipmi HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /favicon.ico HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "POST /api/setup/test-http HTTP/1.1" 200 OK
2026-02-20 19:42:26,004 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json
2026-02-20 19:42:26,006 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json
2026-02-20 19:42:26,179 - fan_controller - INFO - Connected to IPMI at 192.168.5.191
2026-02-20 19:42:26,179 - fan_controller - INFO - HTTP sensor client initialized for http://192.168.5.200:8888
/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:55585 - "POST /api/setup HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET / HTTP/1.1" 200 OK
INFO: 192.168.5.30:53288 - "GET /icons/server-stack.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:55585 - "GET /icons/sun.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:52997 - "GET /icons/wifi.svg HTTP/1.1" 200 OK
INFO: 192.168.5.30:61829 - "GET /icons/arrow-right-on-rectangle.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:59884 - "GET /icons/lock-closed.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:54039 - "GET /icons/clock.svg HTTP/1.1" 304 Not Modified
/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:53288 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:53288 - "GET /icons/document-text.svg HTTP/1.1" 200 OK
INFO: 192.168.5.30:59884 - "GET /icons/viewfinder-circle.svg HTTP/1.1" 200 OK
INFO: 192.168.5.30:61829 - "GET /icons/list-bullet.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:54039 - "GET /icons/chart-bar.svg HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /icons/adjustments-horizontal.svg HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET / HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:55585 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:53971 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:53971 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:53971 - "GET / HTTP/1.1" 200 OK
INFO: 192.168.5.30:53971 - "GET /icons/fire.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:62677 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:60990 - "GET /icons/sun.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:55569 - "GET /icons/lock-closed.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:60990 - "GET /icons/list-bullet.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:62677 - "GET /icons/wifi.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:60301 - "GET /icons/arrow-right-on-rectangle.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:53971 - "GET /icons/clock.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:50652 - "GET /icons/server-stack.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:55569 - "GET /icons/adjustments-horizontal.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:60301 - "GET /icons/chart-bar.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:60990 - "GET /icons/document-text.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:62677 - "GET /icons/viewfinder-circle.svg HTTP/1.1" 304 Not Modified
INFO: 192.168.5.30:62677 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62677 - "GET /login HTTP/1.1" 200 OK
INFO: 192.168.5.30:62677 - "GET /login HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "POST /api/auth/login HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET / HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /favicon.ico HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK
INFO: 192.168.5.30:62355 - "GET /api/status HTTP/1.1" 200 OK

1
static/icons/fan.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.07"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>fan-blades</title><path class="cls-1" d="M67.29,82.9c-.11,1.3-.26,2.6-.47,3.9-1.43,9-5.79,14.34-8.08,22.17C56,118.45,65.32,122.53,73.27,122A37.63,37.63,0,0,0,85,119a45,45,0,0,0,9.32-5.36c20.11-14.8,16-34.9-6.11-46.36a15,15,0,0,0-4.14-1.4,22,22,0,0,1-6,11.07l0,0A22.09,22.09,0,0,1,67.29,82.9ZM62.4,44.22a17.1,17.1,0,1,1-17.1,17.1,17.1,17.1,0,0,1,17.1-17.1ZM84.06,56.83c1.26.05,2.53.14,3.79.29,9.06,1,14.58,5.16,22.5,7.1,9.6,2.35,13.27-7.17,12.41-15.09a37.37,37.37,0,0,0-3.55-11.57,45.35,45.35,0,0,0-5.76-9.08C97.77,9,77.88,14,67.4,36.63a14.14,14.14,0,0,0-1,2.94A22,22,0,0,1,78,45.68l0,0a22.07,22.07,0,0,1,6,11.13Zm-26.9-17c0-1.6.13-3.21.31-4.81,1-9.07,5.12-14.6,7-22.52C66.86,2.89,57.32-.75,49.41.13A37.4,37.4,0,0,0,37.84,3.7a44.58,44.58,0,0,0-9.06,5.78C9.37,25.2,14.39,45.08,37,55.51a14.63,14.63,0,0,0,3.76,1.14A22.12,22.12,0,0,1,57.16,39.83ZM40.66,65.42a52.11,52.11,0,0,1-5.72-.24c-9.08-.88-14.67-4.92-22.62-6.73C2.68,56.25-.83,65.84.16,73.74A37.45,37.45,0,0,0,3.9,85.25a45.06,45.06,0,0,0,5.91,9c16,19.17,35.8,13.87,45.91-8.91a15.93,15.93,0,0,0,.88-2.66A22.15,22.15,0,0,1,40.66,65.42Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
static/icons/favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.07"><path fill="#2196f3" d="M67.29,82.9c-.11,1.3-.26,2.6-.47,3.9-1.43,9-5.79,14.34-8.08,22.17C56,118.45,65.32,122.53,73.27,122A37.63,37.63,0,0,0,85,119a45,45,0,0,0,9.32-5.36c20.11-14.8,16-34.9-6.11-46.36a15,15,0,0,0-4.14-1.4,22,22,0,0,1-6,11.07l0,0A22.09,22.09,0,0,1,67.29,82.9ZM62.4,44.22a17.1,17.1,0,1,1-17.1,17.1,17.1,17.1,0,0,1,17.1-17.1ZM84.06,56.83c1.26.05,2.53.14,3.79.29,9.06,1,14.58,5.16,22.5,7.1,9.6,2.35,13.27-7.17,12.41-15.09a37.37,37.37,0,0,0-3.55-11.57,45.35,45.35,0,0,0-5.76-9.08C97.77,9,77.88,14,67.4,36.63a14.14,14.14,0,0,0-1,2.94A22,22,0,0,1,78,45.68l0,0a22.07,22.07,0,0,1,6,11.13Zm-26.9-17c0-1.6.13-3.21.31-4.81,1-9.07,5.12-14.6,7-22.52C66.86,2.89,57.32-.75,49.41.13A37.4,37.4,0,0,0,37.84,3.7a44.58,44.58,0,0,0-9.06,5.78C9.37,25.2,14.39,45.08,37,55.51a14.63,14.63,0,0,0,3.76,1.14A22.12,22.12,0,0,1,57.16,39.83ZM40.66,65.42a52.11,52.11,0,0,1-5.72-.24c-9.08-.88-14.67-4.92-22.62-6.73C2.68,56.25-.83,65.84.16,73.74A37.45,37.45,0,0,0,3.9,85.25a45.06,45.06,0,0,0,5.91,9c16,19.17,35.8,13.87,45.91-8.91a15.93,15.93,0,0,0,.88-2.66A22.15,22.15,0,0,1,40.66,65.42Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -280,6 +280,7 @@ def get_html(theme="dark"):
.status-item .icon {{ width: 32px; height: 32px; margin: 0 auto 8px; }}
.status-item .icon img {{ width: 100%; height: 100%; filter: brightness(0) invert(1); }}
.icon-svg {{ width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 6px; filter: brightness(0) invert(1); }}
.icon-logo {{ width: 28px; height: 28px; display: inline-block; vertical-align: middle; margin-right: 8px; filter: brightness(0) invert(1); }}
.status-item .label {{ font-size: 0.75rem; color: var(--text-secondary); margin-bottom: 3px; }}
.status-item .value {{ font-size: 1.1rem; font-weight: bold; }}
.status-item .value.good {{ color: var(--accent-success); }}
@ -476,7 +477,7 @@ def get_html(theme="dark"):
<div class="container">
<header>
<div class="header-top">
<h1><img src="/icons/fire.svg" class="icon-svg" alt=""> IPMI Controller</h1>
<h1><img src="/icons/fan.svg" class="icon-logo" alt=""> IPMI Controller</h1>
<div class="header-actions">
<button class="secondary small" onclick="toggleTheme()"><img src="/icons/sun.svg" class="icon-svg" alt=""> Theme</button>
<button class="secondary small" onclick="showPasswordModal()"><img src="/icons/lock-closed.svg" class="icon-svg" alt=""> Password</button>
@ -1178,21 +1179,9 @@ app.mount("/icons", StaticFiles(directory=str(Path(__file__).parent / "static" /
# Routes
@app.get("/favicon.ico")
async def favicon():
"""Transparent 1x1 PNG favicon."""
return Response(
content=bytes([
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89,
0x00, 0x00, 0x00, 0x0D, 0x49, 0x44, 0x41, 0x54,
0x08, 0xD7, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,
0xAE, 0x42, 0x60, 0x82
]),
media_type="image/png"
)
"""Fan icon as favicon."""
icon_path = Path(__file__).parent / "static" / "icons" / "favicon.svg"
return Response(content=icon_path.read_text(), media_type="image/svg+xml")
@app.get("/")
async def root(request: Request):