cpu-loader 0.2.8__tar.gz → 0.4.2__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.4.2}/PKG-INFO +1 -1
  2. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/pyproject.toml +2 -2
  3. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/cpu_loader.py +1 -0
  4. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/main.py +71 -3
  5. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/mqtt_publisher.py +12 -1
  6. cpu_loader-0.4.2/src/cpu_loader/static/favicon.svg +38 -0
  7. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/templates/index.html +23 -0
  8. {cpu_loader-0.2.8 → cpu_loader-0.4.2/src/cpu_loader.egg-info}/PKG-INFO +1 -1
  9. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader.egg-info/SOURCES.txt +1 -0
  10. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/LICENSE +0 -0
  11. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/MANIFEST.in +0 -0
  12. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/README.md +0 -0
  13. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/setup.cfg +0 -0
  14. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/setup.py +0 -0
  15. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/__init__.py +0 -0
  16. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/__main__.py +0 -0
  17. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/cpu_loader_core.c +0 -0
  18. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader/example.py +0 -0
  19. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader.egg-info/dependency_links.txt +0 -0
  20. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader.egg-info/entry_points.txt +0 -0
  21. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/src/cpu_loader.egg-info/requires.txt +0 -0
  22. {cpu_loader-0.2.8 → cpu_loader-0.4.2}/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.4.2
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.4.2"
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"]
@@ -3,6 +3,7 @@ CPU Loader Module
3
3
  Provides CPU load generation with controllable load per thread.
4
4
  Uses a C extension for efficient CPU load generation.
5
5
  """
6
+
6
7
  import multiprocessing
7
8
  from typing import Dict
8
9
 
@@ -2,16 +2,19 @@
2
2
  CPU Loader FastAPI Application
3
3
  Provides REST API and WebUI for controlling CPU load.
4
4
  """
5
+
5
6
  import argparse
6
7
  import asyncio
7
8
  import logging
8
9
  from contextlib import asynccontextmanager
10
+ from pathlib import Path
9
11
  from typing import Dict, List, Optional, Set
10
12
 
11
13
  import psutil
12
14
  import uvicorn
13
15
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
14
- from fastapi.responses import HTMLResponse
16
+ from fastapi.responses import FileResponse, HTMLResponse
17
+ from fastapi.staticfiles import StaticFiles
15
18
  from pydantic import BaseModel, Field
16
19
 
17
20
  from cpu_loader.cpu_loader import CPULoader
@@ -51,6 +54,7 @@ class ThreadsStatusResponse(BaseModel):
51
54
  class CPUMetricsResponse(BaseModel):
52
55
  total_cpu_percent: float
53
56
  per_cpu_percent: List[float]
57
+ cpu_temperature: Optional[float] = None
54
58
 
55
59
 
56
60
  # Global CPU loader instance, MQTT publisher, and WebSocket connections
@@ -58,6 +62,39 @@ cpu_loader = None
58
62
  mqtt_publisher: Optional[MQTTPublisher] = None
59
63
  websocket_connections: Set[WebSocket] = set()
60
64
  monitoring_task = None
65
+ temperature_monitoring_enabled = True
66
+ temperature_monitoring_enabled = True
67
+
68
+
69
+ def get_cpu_temperature() -> Optional[float]:
70
+ """Get CPU temperature if available and enabled."""
71
+ if not temperature_monitoring_enabled:
72
+ return None
73
+
74
+ try:
75
+ # Try to get CPU temperature using psutil
76
+ temperatures = psutil.sensors_temperatures()
77
+
78
+ # Common temperature sensor names to check
79
+ temp_names = ["coretemp", "cpu_thermal", "acpi", "k8temp", "k10temp"]
80
+
81
+ for temp_name in temp_names:
82
+ if temp_name in temperatures:
83
+ # Get the first temperature reading
84
+ temp_list = temperatures[temp_name]
85
+ if temp_list:
86
+ return round(temp_list[0].current, 1)
87
+
88
+ # If no specific sensor found, try the first available
89
+ for sensor_name, temp_list in temperatures.items():
90
+ if temp_list:
91
+ return round(temp_list[0].current, 1)
92
+
93
+ except (AttributeError, OSError, ImportError):
94
+ # Temperature monitoring not available on this system
95
+ pass
96
+
97
+ return None
61
98
 
62
99
 
63
100
  async def cpu_monitoring_loop():
@@ -74,11 +111,15 @@ async def cpu_monitoring_loop():
74
111
  per_cpu = psutil.cpu_percent(interval=None, percpu=True)
75
112
  total_cpu = sum(per_cpu) / len(per_cpu) if per_cpu else 0.0
76
113
 
114
+ # Get CPU temperature if available
115
+ cpu_temp = get_cpu_temperature()
116
+
77
117
  # Prepare message
78
118
  message = {
79
119
  "type": "cpu_metrics",
80
120
  "total_cpu_percent": round(total_cpu, 1),
81
121
  "per_cpu_percent": [round(cpu, 1) for cpu in per_cpu],
122
+ "cpu_temperature": cpu_temp,
82
123
  }
83
124
 
84
125
  # Broadcast to all connected clients
@@ -95,7 +136,7 @@ async def cpu_monitoring_loop():
95
136
 
96
137
  # Publish to MQTT if enabled
97
138
  if mqtt_publisher:
98
- mqtt_publisher.publish_cpu_metrics(total_cpu, per_cpu)
139
+ mqtt_publisher.publish_cpu_metrics(total_cpu, per_cpu, cpu_temp)
99
140
 
100
141
  except Exception as e:
101
142
  logger.error(f"Error in CPU monitoring loop: {e}")
@@ -142,6 +183,21 @@ app = FastAPI(
142
183
  lifespan=lifespan,
143
184
  )
144
185
 
186
+ # Mount static files
187
+ static_path = Path(__file__).parent / "static"
188
+ if static_path.exists():
189
+ app.mount("/static", StaticFiles(directory=static_path), name="static")
190
+
191
+
192
+ @app.get("/favicon.ico")
193
+ async def get_favicon():
194
+ """Serve the favicon."""
195
+ favicon_path = Path(__file__).parent / "static" / "favicon.svg"
196
+ if favicon_path.exists():
197
+ return FileResponse(favicon_path, media_type="image/svg+xml")
198
+ else:
199
+ raise HTTPException(status_code=404, detail="Favicon not found")
200
+
145
201
 
146
202
  @app.get("/", response_class=HTMLResponse)
147
203
  async def get_webui():
@@ -182,7 +238,10 @@ async def get_cpu_metrics():
182
238
  """Get current CPU utilization metrics."""
183
239
  per_cpu = psutil.cpu_percent(interval=0.1, percpu=True)
184
240
  total_cpu = psutil.cpu_percent(interval=0)
185
- return CPUMetricsResponse(total_cpu_percent=total_cpu, per_cpu_percent=per_cpu)
241
+ cpu_temp = get_cpu_temperature()
242
+ return CPUMetricsResponse(
243
+ total_cpu_percent=total_cpu, per_cpu_percent=per_cpu, 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,8 +364,12 @@ 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()
304
369
 
370
+ # Set temperature monitoring based on CLI argument
371
+ temperature_monitoring_enabled = not args.disable_temperature
372
+
305
373
  # Prepare MQTT arguments (only non-None values)
306
374
  mqtt_args = {}
307
375
  if args.mqtt_broker_host:
@@ -2,6 +2,7 @@
2
2
  MQTT Publisher Module
3
3
  Publishes CPU load control settings and metrics to MQTT broker.
4
4
  """
5
+
5
6
  import json
6
7
  import logging
7
8
  import os
@@ -144,13 +145,19 @@ class MQTTPublisher:
144
145
  except Exception as e:
145
146
  logger.error(f"Failed to publish load settings: {e}")
146
147
 
147
- def publish_cpu_metrics(self, total_cpu_percent: float, per_cpu_percent: list):
148
+ def publish_cpu_metrics(
149
+ self,
150
+ total_cpu_percent: float,
151
+ per_cpu_percent: list,
152
+ cpu_temperature: Optional[float] = None,
153
+ ):
148
154
  """
149
155
  Publish CPU metrics to MQTT.
150
156
 
151
157
  Args:
152
158
  total_cpu_percent: Total CPU utilization percentage
153
159
  per_cpu_percent: List of per-CPU utilization percentages
160
+ cpu_temperature: CPU temperature in Celsius (optional)
154
161
  """
155
162
  if not self.connected or not self.client:
156
163
  return
@@ -162,6 +169,10 @@ class MQTTPublisher:
162
169
  "per_cpu_percent": [round(cpu, 1) for cpu in per_cpu_percent],
163
170
  }
164
171
 
172
+ # Add temperature if available
173
+ if cpu_temperature is not None:
174
+ payload["cpu_temperature"] = cpu_temperature
175
+
165
176
  # Publish to topic
166
177
  topic = f"{self.topic_prefix}/cpu_metrics"
167
178
  self.client.publish(
@@ -0,0 +1,38 @@
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
+ <<<<<<< HEAD
35
+ </svg>
36
+ =======
37
+ </svg>
38
+ >>>>>>> 0b500ee (feat: add CPU temperature monitoring and update related UI components)
@@ -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.4.2
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