cpu-loader 0.2.8__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {cpu_loader-0.2.8/src/cpu_loader.egg-info → cpu_loader-0.3.0}/PKG-INFO +1 -1
  2. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/pyproject.toml +2 -2
  3. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/main.py +71 -3
  4. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/mqtt_publisher.py +6 -1
  5. cpu_loader-0.3.0/src/cpu_loader/static/favicon.svg +34 -0
  6. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/templates/index.html +23 -0
  7. {cpu_loader-0.2.8 → cpu_loader-0.3.0/src/cpu_loader.egg-info}/PKG-INFO +1 -1
  8. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader.egg-info/SOURCES.txt +1 -0
  9. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/LICENSE +0 -0
  10. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/MANIFEST.in +0 -0
  11. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/README.md +0 -0
  12. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/setup.cfg +0 -0
  13. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/setup.py +0 -0
  14. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/__init__.py +0 -0
  15. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/__main__.py +0 -0
  16. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/cpu_loader.py +0 -0
  17. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/cpu_loader_core.c +0 -0
  18. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader/example.py +0 -0
  19. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader.egg-info/dependency_links.txt +0 -0
  20. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader.egg-info/entry_points.txt +0 -0
  21. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader.egg-info/requires.txt +0 -0
  22. {cpu_loader-0.2.8 → cpu_loader-0.3.0}/src/cpu_loader.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpu-loader
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API
5
5
  Author-email: the78mole <the78mole@users.noreply.github.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cpu-loader"
3
- version = "0.2.8"
3
+ version = "0.3.0"
4
4
  description = "A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -53,4 +53,4 @@ package-dir = {"" = "src"}
53
53
  packages = ["cpu_loader"]
54
54
 
55
55
  [tool.setuptools.package-data]
56
- cpu_loader = ["templates/*.html", "*.c"]
56
+ cpu_loader = ["templates/*.html", "static/*", "*.c"]
@@ -6,12 +6,14 @@ import argparse
6
6
  import asyncio
7
7
  import logging
8
8
  from contextlib import asynccontextmanager
9
+ from pathlib import Path
9
10
  from typing import Dict, List, Optional, Set
10
11
 
11
12
  import psutil
12
13
  import uvicorn
13
14
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
14
- from fastapi.responses import HTMLResponse
15
+ from fastapi.responses import HTMLResponse, FileResponse
16
+ from fastapi.staticfiles import StaticFiles
15
17
  from pydantic import BaseModel, Field
16
18
 
17
19
  from cpu_loader.cpu_loader import CPULoader
@@ -51,6 +53,7 @@ class ThreadsStatusResponse(BaseModel):
51
53
  class CPUMetricsResponse(BaseModel):
52
54
  total_cpu_percent: float
53
55
  per_cpu_percent: List[float]
56
+ cpu_temperature: Optional[float] = None
54
57
 
55
58
 
56
59
  # Global CPU loader instance, MQTT publisher, and WebSocket connections
@@ -58,6 +61,38 @@ cpu_loader = None
58
61
  mqtt_publisher: Optional[MQTTPublisher] = None
59
62
  websocket_connections: Set[WebSocket] = set()
60
63
  monitoring_task = None
64
+ temperature_monitoring_enabled = True
65
+
66
+
67
+ def get_cpu_temperature() -> Optional[float]:
68
+ """Get CPU temperature if available and enabled."""
69
+ if not temperature_monitoring_enabled:
70
+ return None
71
+
72
+ try:
73
+ # Try to get CPU temperature using psutil
74
+ temperatures = psutil.sensors_temperatures()
75
+
76
+ # Common temperature sensor names to check
77
+ temp_names = ['coretemp', 'cpu_thermal', 'acpi', 'k8temp', 'k10temp']
78
+
79
+ for temp_name in temp_names:
80
+ if temp_name in temperatures:
81
+ # Get the first temperature reading
82
+ temp_list = temperatures[temp_name]
83
+ if temp_list:
84
+ return round(temp_list[0].current, 1)
85
+
86
+ # If no specific sensor found, try the first available
87
+ for sensor_name, temp_list in temperatures.items():
88
+ if temp_list:
89
+ return round(temp_list[0].current, 1)
90
+
91
+ except (AttributeError, OSError, ImportError):
92
+ # Temperature monitoring not available on this system
93
+ pass
94
+
95
+ return None
61
96
 
62
97
 
63
98
  async def cpu_monitoring_loop():
@@ -73,12 +108,16 @@ async def cpu_monitoring_loop():
73
108
  # Get CPU metrics (non-blocking after first call)
74
109
  per_cpu = psutil.cpu_percent(interval=None, percpu=True)
75
110
  total_cpu = sum(per_cpu) / len(per_cpu) if per_cpu else 0.0
111
+
112
+ # Get CPU temperature if available
113
+ cpu_temp = get_cpu_temperature()
76
114
 
77
115
  # Prepare message
78
116
  message = {
79
117
  "type": "cpu_metrics",
80
118
  "total_cpu_percent": round(total_cpu, 1),
81
119
  "per_cpu_percent": [round(cpu, 1) for cpu in per_cpu],
120
+ "cpu_temperature": cpu_temp,
82
121
  }
83
122
 
84
123
  # Broadcast to all connected clients
@@ -95,7 +134,7 @@ async def cpu_monitoring_loop():
95
134
 
96
135
  # Publish to MQTT if enabled
97
136
  if mqtt_publisher:
98
- mqtt_publisher.publish_cpu_metrics(total_cpu, per_cpu)
137
+ mqtt_publisher.publish_cpu_metrics(total_cpu, per_cpu, cpu_temp)
99
138
 
100
139
  except Exception as e:
101
140
  logger.error(f"Error in CPU monitoring loop: {e}")
@@ -142,6 +181,21 @@ app = FastAPI(
142
181
  lifespan=lifespan,
143
182
  )
144
183
 
184
+ # Mount static files
185
+ static_path = Path(__file__).parent / "static"
186
+ if static_path.exists():
187
+ app.mount("/static", StaticFiles(directory=static_path), name="static")
188
+
189
+
190
+ @app.get("/favicon.ico")
191
+ async def get_favicon():
192
+ """Serve the favicon."""
193
+ favicon_path = Path(__file__).parent / "static" / "favicon.svg"
194
+ if favicon_path.exists():
195
+ return FileResponse(favicon_path, media_type="image/svg+xml")
196
+ else:
197
+ raise HTTPException(status_code=404, detail="Favicon not found")
198
+
145
199
 
146
200
  @app.get("/", response_class=HTMLResponse)
147
201
  async def get_webui():
@@ -182,7 +236,12 @@ async def get_cpu_metrics():
182
236
  """Get current CPU utilization metrics."""
183
237
  per_cpu = psutil.cpu_percent(interval=0.1, percpu=True)
184
238
  total_cpu = psutil.cpu_percent(interval=0)
185
- return CPUMetricsResponse(total_cpu_percent=total_cpu, per_cpu_percent=per_cpu)
239
+ cpu_temp = get_cpu_temperature()
240
+ return CPUMetricsResponse(
241
+ total_cpu_percent=total_cpu,
242
+ per_cpu_percent=per_cpu,
243
+ cpu_temperature=cpu_temp
244
+ )
186
245
 
187
246
 
188
247
  @app.post("/api/threads")
@@ -266,6 +325,11 @@ def parse_args():
266
325
  default=8000,
267
326
  help="Port to bind the server to (default: 8000)",
268
327
  )
328
+ parser.add_argument(
329
+ "--disable-temperature",
330
+ action="store_true",
331
+ help="Disable CPU temperature monitoring (useful if temperature sensors are unavailable)",
332
+ )
269
333
 
270
334
  # MQTT arguments
271
335
  mqtt_group = parser.add_argument_group("MQTT settings")
@@ -300,7 +364,11 @@ def parse_args():
300
364
 
301
365
  def run():
302
366
  """Entry point for the CPU Loader application."""
367
+ global temperature_monitoring_enabled
303
368
  args = parse_args()
369
+
370
+ # Set temperature monitoring based on CLI argument
371
+ temperature_monitoring_enabled = not args.disable_temperature
304
372
 
305
373
  # Prepare MQTT arguments (only non-None values)
306
374
  mqtt_args = {}
@@ -144,13 +144,14 @@ class MQTTPublisher:
144
144
  except Exception as e:
145
145
  logger.error(f"Failed to publish load settings: {e}")
146
146
 
147
- def publish_cpu_metrics(self, total_cpu_percent: float, per_cpu_percent: list):
147
+ def publish_cpu_metrics(self, total_cpu_percent: float, per_cpu_percent: list, cpu_temperature: Optional[float] = None):
148
148
  """
149
149
  Publish CPU metrics to MQTT.
150
150
 
151
151
  Args:
152
152
  total_cpu_percent: Total CPU utilization percentage
153
153
  per_cpu_percent: List of per-CPU utilization percentages
154
+ cpu_temperature: CPU temperature in Celsius (optional)
154
155
  """
155
156
  if not self.connected or not self.client:
156
157
  return
@@ -161,6 +162,10 @@ class MQTTPublisher:
161
162
  "total_cpu_percent": round(total_cpu_percent, 1),
162
163
  "per_cpu_percent": [round(cpu, 1) for cpu in per_cpu_percent],
163
164
  }
165
+
166
+ # Add temperature if available
167
+ if cpu_temperature is not None:
168
+ payload["cpu_temperature"] = cpu_temperature
164
169
 
165
170
  # Publish to topic
166
171
  topic = f"{self.topic_prefix}/cpu_metrics"
@@ -0,0 +1,34 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="cpuGradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
5
+ <stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
6
+ </linearGradient>
7
+ </defs>
8
+ <!-- CPU chip outline -->
9
+ <rect x="4" y="4" width="24" height="24" rx="2" ry="2" fill="url(#cpuGradient)" stroke="#333" stroke-width="1"/>
10
+ <!-- CPU pins -->
11
+ <rect x="1" y="8" width="2" height="2" fill="#333"/>
12
+ <rect x="1" y="12" width="2" height="2" fill="#333"/>
13
+ <rect x="1" y="16" width="2" height="2" fill="#333"/>
14
+ <rect x="1" y="20" width="2" height="2" fill="#333"/>
15
+ <rect x="29" y="8" width="2" height="2" fill="#333"/>
16
+ <rect x="29" y="12" width="2" height="2" fill="#333"/>
17
+ <rect x="29" y="16" width="2" height="2" fill="#333"/>
18
+ <rect x="29" y="20" width="2" height="2" fill="#333"/>
19
+ <rect x="8" y="1" width="2" height="2" fill="#333"/>
20
+ <rect x="12" y="1" width="2" height="2" fill="#333"/>
21
+ <rect x="16" y="1" width="2" height="2" fill="#333"/>
22
+ <rect x="20" y="1" width="2" height="2" fill="#333"/>
23
+ <rect x="8" y="29" width="2" height="2" fill="#333"/>
24
+ <rect x="12" y="29" width="2" height="2" fill="#333"/>
25
+ <rect x="16" y="29" width="2" height="2" fill="#333"/>
26
+ <rect x="20" y="29" width="2" height="2" fill="#333"/>
27
+ <!-- CPU core -->
28
+ <rect x="8" y="8" width="16" height="16" rx="1" ry="1" fill="rgba(255,255,255,0.2)"/>
29
+ <!-- Load indicator bars -->
30
+ <rect x="10" y="12" width="2" height="8" fill="#4CAF50"/>
31
+ <rect x="13" y="14" width="2" height="6" fill="#FFC107"/>
32
+ <rect x="16" y="10" width="2" height="10" fill="#F44336"/>
33
+ <rect x="19" y="16" width="2" height="4" fill="#2196F3"/>
34
+ </svg>
@@ -214,6 +214,15 @@
214
214
  <div class="cpu-bar total" id="total-cpu-bar" style="width: 0%"></div>
215
215
  </div>
216
216
  </div>
217
+ <div class="cpu-bar-container" id="cpu-temperature-container" style="display: none;">
218
+ <div class="cpu-bar-label">
219
+ <span><strong>🌡️ CPU Temperature</strong></span>
220
+ <span id="cpu-temperature-value">-</span>
221
+ </div>
222
+ <div class="cpu-bar-wrapper">
223
+ <div class="cpu-bar" id="cpu-temperature-bar" style="width: 0%; background: linear-gradient(90deg, #2196F3 0%, #4CAF50 25%, #FFC107 60%, #FF5722 85%, #B71C1C 100%);"></div>
224
+ </div>
225
+ </div>
217
226
  <div id="per-cpu-bars"></div>
218
227
  </div>
219
228
 
@@ -274,6 +283,20 @@
274
283
  document.getElementById('total-cpu-value').textContent = `${totalCpu}%`;
275
284
  document.getElementById('total-cpu-bar').style.width = `${totalCpu}%`;
276
285
 
286
+ // Update CPU temperature if available
287
+ if (data.cpu_temperature !== null && data.cpu_temperature !== undefined) {
288
+ const tempContainer = document.getElementById('cpu-temperature-container');
289
+ const tempValue = document.getElementById('cpu-temperature-value');
290
+ const tempBar = document.getElementById('cpu-temperature-bar');
291
+
292
+ tempContainer.style.display = 'block';
293
+ tempValue.textContent = `${data.cpu_temperature}°C`;
294
+
295
+ // Scale temperature (0-100°C -> 0-100%)
296
+ const tempPercent = Math.min(100, Math.max(0, data.cpu_temperature));
297
+ tempBar.style.width = `${tempPercent}%`;
298
+ }
299
+
277
300
  // Update per-CPU bars
278
301
  const perCpuContainer = document.getElementById('per-cpu-bars');
279
302
  if (data.per_cpu_percent.length !== numCPUs) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpu-loader
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: A tool to generate CPU load on a system with runtime configuration through a WebUI and REST API
5
5
  Author-email: the78mole <the78mole@users.noreply.github.com>
6
6
  License: MIT License
@@ -16,4 +16,5 @@ src/cpu_loader.egg-info/dependency_links.txt
16
16
  src/cpu_loader.egg-info/entry_points.txt
17
17
  src/cpu_loader.egg-info/requires.txt
18
18
  src/cpu_loader.egg-info/top_level.txt
19
+ src/cpu_loader/static/favicon.svg
19
20
  src/cpu_loader/templates/index.html
File without changes
File without changes
File without changes
File without changes
File without changes