hardwaremon 2.0.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.
- hardwaremon-2.0.0/LICENSE +21 -0
- hardwaremon-2.0.0/PKG-INFO +84 -0
- hardwaremon-2.0.0/README.md +50 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/PKG-INFO +84 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/SOURCES.txt +12 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/dependency_links.txt +1 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/entry_points.txt +2 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/requires.txt +2 -0
- hardwaremon-2.0.0/hardwaremon.egg-info/top_level.txt +1 -0
- hardwaremon-2.0.0/hardwaremonLINUX/HardwareMon.py +1009 -0
- hardwaremon-2.0.0/hardwaremonLINUX/__init__.py +0 -0
- hardwaremon-2.0.0/pyproject.toml +19 -0
- hardwaremon-2.0.0/setup.cfg +4 -0
- hardwaremon-2.0.0/setup.py +17 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Louis Hinchliffe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hardwaremon
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Hardware monitoring tool for CPU, RAM, and battery.
|
|
5
|
+
Author-email: Louis Hinchliffe <your.email@example.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Louis Hinchliffe
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: psutil
|
|
32
|
+
Requires-Dist: requests
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# HardwareMon #
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
HardwareMon is a lightweight system monitoring tool designed to provide a detailed overview of your computer’s hardware and performance metrics. It can display CPU, memory, disk, GPU, battery, network, and peripheral information in real time. The project includes two main implementations: a Python version for Linux systems and a PowerShell version for Windows.
|
|
39
|
+
|
|
40
|
+
## Features ##
|
|
41
|
+
|
|
42
|
+
HardwareMon gathers and presents information such as CPU usage, memory usage, disk activity, top running processes, GPU specifications and temperatures, battery status, and peripheral details. On Linux, the Python version uses the psutil library and native system commands to extract detailed system information. On Windows, the PowerShell version leverages CIM/WMI queries to report similar statistics.
|
|
43
|
+
|
|
44
|
+
The Linux version also includes a GUI mode built with Tkinter, offering a modern and configurable interface with light, dark, and hacker-style themes. To cycle themes, press F2. Graphs for CPU, memory, and disk usage are updated in real time, providing a quick visual snapshot of system performance.
|
|
45
|
+
|
|
46
|
+
## Installation ##
|
|
47
|
+
Linux (Python version)
|
|
48
|
+
|
|
49
|
+
Ensure Python 3 and the psutil library are installed.
|
|
50
|
+
|
|
51
|
+
Clone the repository or download the Python script.
|
|
52
|
+
|
|
53
|
+
Run the script from a terminal:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
python3 hardware_mon.py
|
|
57
|
+
```
|
|
58
|
+
## Windows (PowerShell version) ##
|
|
59
|
+
|
|
60
|
+
Open PowerShell.
|
|
61
|
+
|
|
62
|
+
Run the script directly or use the provided .exe if available:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
.\hardware-info.ps1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The Windows .exe was generated from the PowerShell script but is not maintained as actively as the source script. It may lack the latest bug fixes or features and is provided primarily for convenience.
|
|
69
|
+
|
|
70
|
+
# Notes #
|
|
71
|
+
|
|
72
|
+
The Windows version relies on CIM/WMI queries and may have limitations on certain hardware or older operating systems. The Linux Python version provides more extensive monitoring capabilities, including GPU lane and VRAM information for NVIDIA and AMD cards, as well as real-time graphical representations.
|
|
73
|
+
|
|
74
|
+
While the project can run as a standalone script or executable, it is recommended to use the scripts directly to ensure maximum compatibility and access to the latest updates.
|
|
75
|
+
|
|
76
|
+
The .sh script is also much more basic compared to the .py script on linux, since the .py script relies on Tkinter for graph drawings which Bash cannot use, and has not had as much attention compared to the .py version.
|
|
77
|
+
|
|
78
|
+
The .yml script only runs on the .py script.
|
|
79
|
+
|
|
80
|
+
# Contribution #
|
|
81
|
+
|
|
82
|
+
Contributions and suggestions are welcome. Please feel free to fork the repository and submit pull requests or report issues on the project’s issue tracker.
|
|
83
|
+
|
|
84
|
+
Made with ❤️ by Louis
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# HardwareMon #
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
HardwareMon is a lightweight system monitoring tool designed to provide a detailed overview of your computer’s hardware and performance metrics. It can display CPU, memory, disk, GPU, battery, network, and peripheral information in real time. The project includes two main implementations: a Python version for Linux systems and a PowerShell version for Windows.
|
|
5
|
+
|
|
6
|
+
## Features ##
|
|
7
|
+
|
|
8
|
+
HardwareMon gathers and presents information such as CPU usage, memory usage, disk activity, top running processes, GPU specifications and temperatures, battery status, and peripheral details. On Linux, the Python version uses the psutil library and native system commands to extract detailed system information. On Windows, the PowerShell version leverages CIM/WMI queries to report similar statistics.
|
|
9
|
+
|
|
10
|
+
The Linux version also includes a GUI mode built with Tkinter, offering a modern and configurable interface with light, dark, and hacker-style themes. To cycle themes, press F2. Graphs for CPU, memory, and disk usage are updated in real time, providing a quick visual snapshot of system performance.
|
|
11
|
+
|
|
12
|
+
## Installation ##
|
|
13
|
+
Linux (Python version)
|
|
14
|
+
|
|
15
|
+
Ensure Python 3 and the psutil library are installed.
|
|
16
|
+
|
|
17
|
+
Clone the repository or download the Python script.
|
|
18
|
+
|
|
19
|
+
Run the script from a terminal:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
python3 hardware_mon.py
|
|
23
|
+
```
|
|
24
|
+
## Windows (PowerShell version) ##
|
|
25
|
+
|
|
26
|
+
Open PowerShell.
|
|
27
|
+
|
|
28
|
+
Run the script directly or use the provided .exe if available:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
.\hardware-info.ps1
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The Windows .exe was generated from the PowerShell script but is not maintained as actively as the source script. It may lack the latest bug fixes or features and is provided primarily for convenience.
|
|
35
|
+
|
|
36
|
+
# Notes #
|
|
37
|
+
|
|
38
|
+
The Windows version relies on CIM/WMI queries and may have limitations on certain hardware or older operating systems. The Linux Python version provides more extensive monitoring capabilities, including GPU lane and VRAM information for NVIDIA and AMD cards, as well as real-time graphical representations.
|
|
39
|
+
|
|
40
|
+
While the project can run as a standalone script or executable, it is recommended to use the scripts directly to ensure maximum compatibility and access to the latest updates.
|
|
41
|
+
|
|
42
|
+
The .sh script is also much more basic compared to the .py script on linux, since the .py script relies on Tkinter for graph drawings which Bash cannot use, and has not had as much attention compared to the .py version.
|
|
43
|
+
|
|
44
|
+
The .yml script only runs on the .py script.
|
|
45
|
+
|
|
46
|
+
# Contribution #
|
|
47
|
+
|
|
48
|
+
Contributions and suggestions are welcome. Please feel free to fork the repository and submit pull requests or report issues on the project’s issue tracker.
|
|
49
|
+
|
|
50
|
+
Made with ❤️ by Louis
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hardwaremon
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Hardware monitoring tool for CPU, RAM, and battery.
|
|
5
|
+
Author-email: Louis Hinchliffe <your.email@example.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Louis Hinchliffe
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: psutil
|
|
32
|
+
Requires-Dist: requests
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# HardwareMon #
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
HardwareMon is a lightweight system monitoring tool designed to provide a detailed overview of your computer’s hardware and performance metrics. It can display CPU, memory, disk, GPU, battery, network, and peripheral information in real time. The project includes two main implementations: a Python version for Linux systems and a PowerShell version for Windows.
|
|
39
|
+
|
|
40
|
+
## Features ##
|
|
41
|
+
|
|
42
|
+
HardwareMon gathers and presents information such as CPU usage, memory usage, disk activity, top running processes, GPU specifications and temperatures, battery status, and peripheral details. On Linux, the Python version uses the psutil library and native system commands to extract detailed system information. On Windows, the PowerShell version leverages CIM/WMI queries to report similar statistics.
|
|
43
|
+
|
|
44
|
+
The Linux version also includes a GUI mode built with Tkinter, offering a modern and configurable interface with light, dark, and hacker-style themes. To cycle themes, press F2. Graphs for CPU, memory, and disk usage are updated in real time, providing a quick visual snapshot of system performance.
|
|
45
|
+
|
|
46
|
+
## Installation ##
|
|
47
|
+
Linux (Python version)
|
|
48
|
+
|
|
49
|
+
Ensure Python 3 and the psutil library are installed.
|
|
50
|
+
|
|
51
|
+
Clone the repository or download the Python script.
|
|
52
|
+
|
|
53
|
+
Run the script from a terminal:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
python3 hardware_mon.py
|
|
57
|
+
```
|
|
58
|
+
## Windows (PowerShell version) ##
|
|
59
|
+
|
|
60
|
+
Open PowerShell.
|
|
61
|
+
|
|
62
|
+
Run the script directly or use the provided .exe if available:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
.\hardware-info.ps1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The Windows .exe was generated from the PowerShell script but is not maintained as actively as the source script. It may lack the latest bug fixes or features and is provided primarily for convenience.
|
|
69
|
+
|
|
70
|
+
# Notes #
|
|
71
|
+
|
|
72
|
+
The Windows version relies on CIM/WMI queries and may have limitations on certain hardware or older operating systems. The Linux Python version provides more extensive monitoring capabilities, including GPU lane and VRAM information for NVIDIA and AMD cards, as well as real-time graphical representations.
|
|
73
|
+
|
|
74
|
+
While the project can run as a standalone script or executable, it is recommended to use the scripts directly to ensure maximum compatibility and access to the latest updates.
|
|
75
|
+
|
|
76
|
+
The .sh script is also much more basic compared to the .py script on linux, since the .py script relies on Tkinter for graph drawings which Bash cannot use, and has not had as much attention compared to the .py version.
|
|
77
|
+
|
|
78
|
+
The .yml script only runs on the .py script.
|
|
79
|
+
|
|
80
|
+
# Contribution #
|
|
81
|
+
|
|
82
|
+
Contributions and suggestions are welcome. Please feel free to fork the repository and submit pull requests or report issues on the project’s issue tracker.
|
|
83
|
+
|
|
84
|
+
Made with ❤️ by Louis
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
hardwaremon.egg-info/PKG-INFO
|
|
6
|
+
hardwaremon.egg-info/SOURCES.txt
|
|
7
|
+
hardwaremon.egg-info/dependency_links.txt
|
|
8
|
+
hardwaremon.egg-info/entry_points.txt
|
|
9
|
+
hardwaremon.egg-info/requires.txt
|
|
10
|
+
hardwaremon.egg-info/top_level.txt
|
|
11
|
+
hardwaremonLINUX/HardwareMon.py
|
|
12
|
+
hardwaremonLINUX/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hardwaremonLINUX
|
|
@@ -0,0 +1,1009 @@
|
|
|
1
|
+
from email.mime import text
|
|
2
|
+
from logging import root
|
|
3
|
+
import platform
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
import shutil
|
|
7
|
+
import re
|
|
8
|
+
import requests
|
|
9
|
+
import psutil
|
|
10
|
+
import tkinter.messagebox as messagebox
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
VERSION = "2.0.0"
|
|
14
|
+
|
|
15
|
+
MAX_POINTS = 60 # last 60 seconds
|
|
16
|
+
|
|
17
|
+
cpu_history = []
|
|
18
|
+
mem_history = []
|
|
19
|
+
disk_history = []
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
import requests
|
|
23
|
+
|
|
24
|
+
def check_for_updates():
|
|
25
|
+
repo = "louisboii747/HardwareMon"
|
|
26
|
+
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
response = requests.get(url, timeout=5)
|
|
30
|
+
if response.status_code == 200:
|
|
31
|
+
latest_version = response.json()["tag_name"].lstrip("v")
|
|
32
|
+
if latest_version != VERSION:
|
|
33
|
+
return f"Update available: v{latest_version}"
|
|
34
|
+
return None # No update
|
|
35
|
+
except Exception:
|
|
36
|
+
return None # Fail silently
|
|
37
|
+
|
|
38
|
+
def update_history():
|
|
39
|
+
cpu = psutil.cpu_percent(interval=None)
|
|
40
|
+
mem = psutil.virtual_memory().percent
|
|
41
|
+
disk = psutil.disk_usage('/').percent
|
|
42
|
+
|
|
43
|
+
cpu_history.append(cpu)
|
|
44
|
+
mem_history.append(mem)
|
|
45
|
+
disk_history.append(disk)
|
|
46
|
+
|
|
47
|
+
if len(cpu_history) > MAX_POINTS:
|
|
48
|
+
cpu_history.pop(0)
|
|
49
|
+
mem_history.pop(0)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def draw_graph(canvas, data, color, label):
|
|
53
|
+
canvas.delete("all")
|
|
54
|
+
|
|
55
|
+
width = int(canvas["width"])
|
|
56
|
+
height = int(canvas["height"])
|
|
57
|
+
padding = 10
|
|
58
|
+
|
|
59
|
+
if len(data) < 2:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
max_val = 100 # percent-based graph
|
|
63
|
+
step_x = (width - 2 * padding) / (len(data) - 1)
|
|
64
|
+
|
|
65
|
+
points = []
|
|
66
|
+
for i, value in enumerate(data):
|
|
67
|
+
x = padding + i * step_x
|
|
68
|
+
y = height - padding - (value / max_val) * (height - 2 * padding)
|
|
69
|
+
points.extend([x, y])
|
|
70
|
+
|
|
71
|
+
canvas.create_line(points, fill=color, width=2, smooth=True)
|
|
72
|
+
canvas.create_text(
|
|
73
|
+
5, 5,
|
|
74
|
+
anchor="nw",
|
|
75
|
+
text=f"{label}: {data[-1]:.1f}%",
|
|
76
|
+
fill=color,
|
|
77
|
+
font=("monospace", 10, "bold")
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
ALERTS = {
|
|
82
|
+
"cpu": 90,
|
|
83
|
+
"memory": 90,
|
|
84
|
+
"gpu_temp": 80,
|
|
85
|
+
"battery_low": 20
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
def check_alerts():
|
|
89
|
+
alerts = []
|
|
90
|
+
cpu = psutil.cpu_percent(interval=None)
|
|
91
|
+
if cpu > ALERTS["cpu"]:
|
|
92
|
+
alerts.append(f"⚠️ CPU Usage High: {cpu:.1f}%")
|
|
93
|
+
|
|
94
|
+
mem = psutil.virtual_memory().percent
|
|
95
|
+
if mem > ALERTS["memory"]:
|
|
96
|
+
alerts.append(f"⚠️ Memory Usage High: {mem:.1f}%")
|
|
97
|
+
|
|
98
|
+
temps = psutil.sensors_temperatures()
|
|
99
|
+
if temps:
|
|
100
|
+
for name, entries in temps.items():
|
|
101
|
+
for entry in entries:
|
|
102
|
+
if "gpu" in entry.label.lower() or "nvidia" in name.lower():
|
|
103
|
+
if entry.current > ALERTS["gpu_temp"]:
|
|
104
|
+
alerts.append(f"⚠️ GPU Temp High: {entry.current} °C")
|
|
105
|
+
|
|
106
|
+
bat = psutil.sensors_battery()
|
|
107
|
+
if bat and not bat.power_plugged and bat.percent < ALERTS["battery_low"]:
|
|
108
|
+
alerts.append(f"⚠️ Battery Low: {bat.percent:.1f}%")
|
|
109
|
+
|
|
110
|
+
return alerts
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def gpu_info():
|
|
114
|
+
lines = ["=== GPU Information ==="]
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
if platform.system() != "Linux":
|
|
118
|
+
return ["GPU info not implemented for this OS."]
|
|
119
|
+
|
|
120
|
+
# 1. Find ONLY dedicated AMD / NVIDIA GPUs
|
|
121
|
+
cmd = (
|
|
122
|
+
"lspci | grep -Ei "
|
|
123
|
+
"'(NVIDIA Corporation|Advanced Micro Devices, Inc.)' | "
|
|
124
|
+
"grep -E '(VGA|3D)' | "
|
|
125
|
+
"grep -vi 'APU'"
|
|
126
|
+
)
|
|
127
|
+
gpus = [l for l in os.popen(cmd).read().splitlines() if l]
|
|
128
|
+
|
|
129
|
+
if not gpus:
|
|
130
|
+
return ["No dedicated AMD or NVIDIA GPU found."]
|
|
131
|
+
|
|
132
|
+
for gpu in gpus:
|
|
133
|
+
lines.append(gpu)
|
|
134
|
+
|
|
135
|
+
# Extract PCI bus ID (e.g. 01:00.0)
|
|
136
|
+
bus_id = gpu.split()[0]
|
|
137
|
+
|
|
138
|
+
# 2. PCIe lane width (current + max)
|
|
139
|
+
pcie_info = os.popen(f"lspci -s {bus_id} -vv").read()
|
|
140
|
+
lane_match = re.search(
|
|
141
|
+
r"LnkSta:\s+Speed\s+[^,]+,\s+Width\s+x(\d+).*?\n.*?LnkCap:\s+Speed\s+[^,]+,\s+Width\s+x(\d+)",
|
|
142
|
+
pcie_info,
|
|
143
|
+
re.S
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if lane_match:
|
|
147
|
+
current, maximum = lane_match.groups()
|
|
148
|
+
lines.append(f" PCIe: x{current} (max x{maximum})")
|
|
149
|
+
else:
|
|
150
|
+
lines.append(" PCIe: Unknown")
|
|
151
|
+
|
|
152
|
+
# 3. VRAM detection
|
|
153
|
+
if "NVIDIA" in gpu:
|
|
154
|
+
vram = os.popen(
|
|
155
|
+
"nvidia-smi --query-gpu=memory.total "
|
|
156
|
+
"--format=csv,noheader,nounits"
|
|
157
|
+
).read().strip()
|
|
158
|
+
if vram:
|
|
159
|
+
lines.append(f" VRAM: {vram} MB")
|
|
160
|
+
else:
|
|
161
|
+
lines.append(" VRAM: Unknown")
|
|
162
|
+
|
|
163
|
+
elif "Advanced Micro Devices" in gpu:
|
|
164
|
+
vram = os.popen(
|
|
165
|
+
"grep -i 'VRAM' /var/log/Xorg.0.log 2>/dev/null | head -n1"
|
|
166
|
+
).read().strip()
|
|
167
|
+
if vram:
|
|
168
|
+
lines.append(f" VRAM: {vram}")
|
|
169
|
+
else:
|
|
170
|
+
lines.append(" VRAM: Unknown (AMD userspace tools vary)")
|
|
171
|
+
|
|
172
|
+
return lines
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
return [f"GPU info error: {e}"]
|
|
176
|
+
mem = psutil.virtual_memory().percent
|
|
177
|
+
if mem > ALERTS["memory"]:
|
|
178
|
+
alerts = []
|
|
179
|
+
alerts.append(f"⚠️ Memory Usage High: {mem:.1f}%")
|
|
180
|
+
|
|
181
|
+
# GPU temp (if available)
|
|
182
|
+
temps = psutil.sensors_temperatures()
|
|
183
|
+
if temps:
|
|
184
|
+
for name, entries in temps.items():
|
|
185
|
+
for entry in entries:
|
|
186
|
+
if "gpu" in entry.label.lower() or "nvidia" in name.lower():
|
|
187
|
+
if entry.current > ALERTS["gpu_temp"]:
|
|
188
|
+
alerts = []
|
|
189
|
+
alerts.append(f"⚠️ GPU Temp High: {entry.current} °C")
|
|
190
|
+
|
|
191
|
+
# Battery
|
|
192
|
+
bat = psutil.sensors_battery()
|
|
193
|
+
if bat and not bat.power_plugged and bat.percent < ALERTS["battery_low"]:
|
|
194
|
+
alerts = []
|
|
195
|
+
alerts.append(f"⚠️ Battery Low: {bat.percent:.1f}%")
|
|
196
|
+
|
|
197
|
+
return alerts
|
|
198
|
+
|
|
199
|
+
def get_cpu_name():
|
|
200
|
+
if platform.system() == "Linux":
|
|
201
|
+
try:
|
|
202
|
+
with open("/proc/cpuinfo") as f:
|
|
203
|
+
for line in f:
|
|
204
|
+
if "model name" in line:
|
|
205
|
+
return line.split(":", 1)[1].strip()
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
return platform.processor()
|
|
209
|
+
|
|
210
|
+
THEMES = {
|
|
211
|
+
"dark": {
|
|
212
|
+
"bg": "#111111",
|
|
213
|
+
"fg": "#e6e6e6",
|
|
214
|
+
"accent": "#4fc3f7"
|
|
215
|
+
},
|
|
216
|
+
"hacker": {
|
|
217
|
+
"bg": "#000000",
|
|
218
|
+
"fg": "#00ff00",
|
|
219
|
+
"accent": "#00cc00"
|
|
220
|
+
},
|
|
221
|
+
"red": {
|
|
222
|
+
"bg": "#2e0000",
|
|
223
|
+
"fg": "#ff4d4d",
|
|
224
|
+
"accent": "#ff1a1a"
|
|
225
|
+
},
|
|
226
|
+
"black on white": {
|
|
227
|
+
"bg": "#ffffff",
|
|
228
|
+
"fg": "#000000",
|
|
229
|
+
"accent": "#000000"
|
|
230
|
+
},
|
|
231
|
+
"blue": {
|
|
232
|
+
"bg": "#001f3f",
|
|
233
|
+
"fg": "#7FDBFF",
|
|
234
|
+
"accent": "#FFFFFF"
|
|
235
|
+
},
|
|
236
|
+
"purple": {
|
|
237
|
+
"bg": "#2e003e",
|
|
238
|
+
"fg": "#ff66ff",
|
|
239
|
+
"accent": "#ff1aff"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def apply_theme(root, text, theme_name):
|
|
247
|
+
theme = THEMES[theme_name]
|
|
248
|
+
root.configure(bg=theme["bg"])
|
|
249
|
+
text.configure(
|
|
250
|
+
bg=theme["bg"],
|
|
251
|
+
fg=theme["fg"],
|
|
252
|
+
insertbackground=theme["fg"] # caret color
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
current_theme = "dark" # default
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def read_sys(path):
|
|
260
|
+
try:
|
|
261
|
+
with open(path) as f:
|
|
262
|
+
return f.read().strip()
|
|
263
|
+
except Exception:
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
def battery_info():
|
|
267
|
+
lines = ["=== Battery Information ==="]
|
|
268
|
+
if not hasattr(psutil, "sensors_battery"):
|
|
269
|
+
return None
|
|
270
|
+
bat = psutil.sensors_battery()
|
|
271
|
+
if bat is None:
|
|
272
|
+
return None
|
|
273
|
+
percent = bat.percent
|
|
274
|
+
plugged = bat.power_plugged
|
|
275
|
+
secsleft = bat.secsleft
|
|
276
|
+
if secsleft == psutil.POWER_TIME_UNLIMITED:
|
|
277
|
+
time_str = "N/A"
|
|
278
|
+
elif secsleft == psutil.POWER_TIME_UNKNOWN:
|
|
279
|
+
time_str = "Unknown"
|
|
280
|
+
else:
|
|
281
|
+
hours, remainder = divmod(secsleft, 3600)
|
|
282
|
+
minutes, _ = divmod(remainder, 60)
|
|
283
|
+
time_str = f"{hours}h {minutes}m"
|
|
284
|
+
return f"{percent}% {'(Charging)' if plugged else '(Discharging)'} - Time left: {time_str}"
|
|
285
|
+
return lines
|
|
286
|
+
|
|
287
|
+
def clear_screen():
|
|
288
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
## START SYSTEM SUMMARY ##
|
|
292
|
+
|
|
293
|
+
# ---------- Summary Function ----------
|
|
294
|
+
def system_summary():
|
|
295
|
+
lines = ["=== SYSTEM SUMMARY ==="]
|
|
296
|
+
|
|
297
|
+
cpu_name = get_cpu_name()
|
|
298
|
+
total_ram = round(psutil.virtual_memory().total / (1024**3), 1)
|
|
299
|
+
disk_total = round(psutil.disk_usage('/').total / (1024**3), 1)
|
|
300
|
+
|
|
301
|
+
# GPU name (simplified)
|
|
302
|
+
gpu_name = "Unknown"
|
|
303
|
+
if platform.system() == "Linux":
|
|
304
|
+
output = os.popen(
|
|
305
|
+
"lspci | grep -Ei '(VGA|3D)' | grep -Ei '(NVIDIA|AMD)'"
|
|
306
|
+
).read().strip()
|
|
307
|
+
if output:
|
|
308
|
+
gpu_name = output.split(":")[-1].strip()
|
|
309
|
+
|
|
310
|
+
lines.append(f"CPU : {cpu_name}")
|
|
311
|
+
lines.append(f"GPU : {gpu_name}")
|
|
312
|
+
lines.append(f"RAM : {total_ram} GB")
|
|
313
|
+
lines.append(f"Disk : {disk_total} GB\n")
|
|
314
|
+
|
|
315
|
+
cpu = psutil.cpu_percent(interval=None)
|
|
316
|
+
mem = psutil.virtual_memory().percent
|
|
317
|
+
disk = psutil.disk_usage('/').percent
|
|
318
|
+
|
|
319
|
+
lines.append(f"CPU Usage : {cpu:.1f}%")
|
|
320
|
+
lines.append(f"Memory Usage: {mem:.1f}%")
|
|
321
|
+
lines.append(f"Disk Usage : {disk:.1f}%")
|
|
322
|
+
|
|
323
|
+
return lines
|
|
324
|
+
|
|
325
|
+
# ---------- Full Sections ---------- #
|
|
326
|
+
|
|
327
|
+
SECTIONS = [
|
|
328
|
+
system_summary
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
## END System Summary ##
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def system_info():
|
|
337
|
+
lines = ["=== System Information ==="]
|
|
338
|
+
try:
|
|
339
|
+
uptime_seconds = time.time() - psutil.boot_time()
|
|
340
|
+
info = [
|
|
341
|
+
f"OS/Kernel Version: {platform.system()} {platform.release()}",
|
|
342
|
+
f"Architecture: {platform.machine()}",
|
|
343
|
+
f"CPU: {get_cpu_name()}",
|
|
344
|
+
f"CPU Frequency: {psutil.cpu_freq().current:.2f} MHz",
|
|
345
|
+
f"CPU Cores: {psutil.cpu_count(logical=False)}",
|
|
346
|
+
f"Threads: {psutil.cpu_count(logical=True)}",
|
|
347
|
+
f"Memory: {round(psutil.virtual_memory().total / (1024**3), 2)} GB",
|
|
348
|
+
f"Disk: {round(psutil.disk_usage('/').total / (1024**3), 2)} GB",
|
|
349
|
+
f"Uptime: {uptime_seconds / 3600:.2f} hours",
|
|
350
|
+
f"User: {os.getlogin()}",
|
|
351
|
+
f"Display Size: {shutil.get_terminal_size().columns}x{shutil.get_terminal_size().lines}",
|
|
352
|
+
f"Filesystem: {platform.system()}"
|
|
353
|
+
f"Resizable Bar: {'Supported' if os.path.exists('/sys/bus/pci/devices/0000:00:01.0/resizable_bar') else 'Not Supported'}"
|
|
354
|
+
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
io = psutil.disk_io_counters()
|
|
358
|
+
if io:
|
|
359
|
+
info.append(
|
|
360
|
+
f"Disk Activity: {io.read_bytes / (1024**2):.2f} MB read, "
|
|
361
|
+
f"{io.write_bytes / (1024**2):.2f} MB written"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
bat = battery_info()
|
|
365
|
+
info.append(f"Battery: {bat if bat else 'N/A'}")
|
|
366
|
+
|
|
367
|
+
return info
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
return [f"System info error: {e}"]
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
## END System Info ##
|
|
374
|
+
|
|
375
|
+
def swap_memory():
|
|
376
|
+
lines = ["=== Swap Memory ==="]
|
|
377
|
+
swap = psutil.swap_memory()
|
|
378
|
+
lines.append(f"Swap: {round(swap.total / (1024**3), 2)} GB, Used: {round(swap.used / (1024**3), 2)} GB ({swap.percent}%)")
|
|
379
|
+
return lines
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def memory_temperature():
|
|
386
|
+
lines = ["=== Memory Temperature ==="]
|
|
387
|
+
temps = psutil.sensors_temperatures()
|
|
388
|
+
if not temps:
|
|
389
|
+
return ["===Memory temperature sensors not available"]
|
|
390
|
+
|
|
391
|
+
for name, entries in temps.items():
|
|
392
|
+
for entry in entries:
|
|
393
|
+
if "memory" in entry.label.lower() or "ram" in name.lower():
|
|
394
|
+
if entry.label:
|
|
395
|
+
lines.append(f"{entry.label}: {entry.current} °C")
|
|
396
|
+
else:
|
|
397
|
+
lines.append(f"{name}: {entry.current} °C")
|
|
398
|
+
|
|
399
|
+
return lines if len(lines) > 1 else ["===No Memory temperature data found==="]
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def gpu_info():
|
|
403
|
+
lines = ["=== GPU Information ==="]
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
if platform.system() != "Linux":
|
|
407
|
+
return ["GPU info not implemented for this OS."]
|
|
408
|
+
|
|
409
|
+
# 1. Find ONLY dedicated AMD / NVIDIA GPUs
|
|
410
|
+
cmd = (
|
|
411
|
+
"lspci | grep -Ei "
|
|
412
|
+
"'(NVIDIA Corporation|Advanced Micro Devices, Inc.)' | "
|
|
413
|
+
"'(VGA|3D)' | "
|
|
414
|
+
"'(APU)' -vi"
|
|
415
|
+
"grep -E '(VGA|3D)' | "
|
|
416
|
+
"grep -vi 'APU'"
|
|
417
|
+
)
|
|
418
|
+
gpus = [l for l in os.popen(cmd).read().splitlines() if l]
|
|
419
|
+
|
|
420
|
+
if not gpus:
|
|
421
|
+
return ["No dedicated AMD or NVIDIA GPU found."]
|
|
422
|
+
|
|
423
|
+
for gpu in gpus:
|
|
424
|
+
lines.append(gpu)
|
|
425
|
+
|
|
426
|
+
# Extract PCI bus ID (e.g. 01:00.0)
|
|
427
|
+
bus_id = gpu.split()[0]
|
|
428
|
+
|
|
429
|
+
# 2. PCIe lane width (current + max)
|
|
430
|
+
pcie_info = os.popen(f"lspci -s {bus_id} -vv").read()
|
|
431
|
+
lane_match = re.search(
|
|
432
|
+
r"LnkSta:\s+Speed\s+[^,]+,\s+Width\s+x(\d+).*?\n.*?LnkCap:\s+Speed\s+[^,]+,\s+Width\s+x(\d+)",
|
|
433
|
+
pcie_info,
|
|
434
|
+
re.S
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
if lane_match:
|
|
438
|
+
current, maximum = lane_match.groups()
|
|
439
|
+
lines.append(f" PCIe: x{current} (max x{maximum})")
|
|
440
|
+
else:
|
|
441
|
+
lines.append(" PCIe: Unknown")
|
|
442
|
+
|
|
443
|
+
# 3. VRAM detection
|
|
444
|
+
if "NVIDIA" in gpu:
|
|
445
|
+
vram = os.popen(
|
|
446
|
+
"nvidia-smi --query-gpu=memory.total "
|
|
447
|
+
"--format=csv,noheader,nounits"
|
|
448
|
+
).read().strip()
|
|
449
|
+
if vram:
|
|
450
|
+
lines.append(f" VRAM: {vram} MB")
|
|
451
|
+
else:
|
|
452
|
+
lines.append(" VRAM: Unknown")
|
|
453
|
+
|
|
454
|
+
elif "Advanced Micro Devices" in gpu:
|
|
455
|
+
vram = os.popen(
|
|
456
|
+
"grep -i 'VRAM' /var/log/Xorg.0.log 2>/dev/null | head -n1"
|
|
457
|
+
).read().strip()
|
|
458
|
+
if vram:
|
|
459
|
+
lines.append(f" VRAM: {vram}")
|
|
460
|
+
else:
|
|
461
|
+
lines.append(" VRAM: Unknown (AMD userspace tools vary)")
|
|
462
|
+
|
|
463
|
+
return lines
|
|
464
|
+
|
|
465
|
+
except Exception as e:
|
|
466
|
+
return [f"GPU info error: {e}"]
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def intel_gpu_info():
|
|
473
|
+
lines = ["=== Intel GPU Information ==="]
|
|
474
|
+
try:
|
|
475
|
+
if platform.system() == "Linux":
|
|
476
|
+
intel_gpu_output = os.popen("lspci | grep -i 'intel' | grep -i 'vga\\|3d\\|2d'").read()
|
|
477
|
+
gpus = intel_gpu_output.strip().split("\n")
|
|
478
|
+
if not gpus or gpus == ['']:
|
|
479
|
+
return ["No Intel GPU information found."]
|
|
480
|
+
for gpu in gpus:
|
|
481
|
+
lines.append(gpu)
|
|
482
|
+
else:
|
|
483
|
+
lines.append("Intel GPU info not implemented for this OS.")
|
|
484
|
+
except Exception as e:
|
|
485
|
+
lines.append(f"Intel GPU info error: {e}")
|
|
486
|
+
return lines
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def cpu_mem_bar():
|
|
490
|
+
cpu = psutil.cpu_percent(interval=None)
|
|
491
|
+
lines = ["=== CPU and Memory Usage ==="]
|
|
492
|
+
mem = psutil.virtual_memory().percent
|
|
493
|
+
width = 50
|
|
494
|
+
cpu_bar = "#" * int(cpu / 100 * width)
|
|
495
|
+
mem_bar = "#" * int(mem / 100 * width)
|
|
496
|
+
lines.append(f"CPU: [{cpu_bar:<{width}}] {cpu:.1f}%")
|
|
497
|
+
lines.append(f"MEM: [{mem_bar:<{width}}] {mem:.1f}%")
|
|
498
|
+
return lines
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def network_info():
|
|
502
|
+
net = psutil.net_io_counters(pernic=True)
|
|
503
|
+
lines = ["=== Network Interfaces ==="]
|
|
504
|
+
for iface, data in net.items():
|
|
505
|
+
lines.append(f"{iface:10}: Sent={data.bytes_sent / (1024**2):6.2f} MB | Recv={data.bytes_recv / (1024**2):6.2f} MB")
|
|
506
|
+
return lines
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def top_processes(n=5):
|
|
511
|
+
procs = [(p.info["name"], p.info["cpu_percent"], p.info["memory_percent"])
|
|
512
|
+
for p in psutil.process_iter(["name", "cpu_percent", "memory_percent"])]
|
|
513
|
+
procs.sort(key=lambda x: (x[1], x[2]), reverse=True)
|
|
514
|
+
lines = [f"=== Top {n} Processes ==="]
|
|
515
|
+
for name, cpu, mem in procs[:n]:
|
|
516
|
+
lines.append(f"{name[:25]:25} | CPU: {cpu:5.1f}% | MEM: {mem:5.1f}%")
|
|
517
|
+
return lines
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def motherboard_info():
|
|
523
|
+
if platform.system() == "Linux":
|
|
524
|
+
lines = ["=== Motherboard Information ==="]
|
|
525
|
+
base_path = "/sys/devices/virtual/dmi/id/"
|
|
526
|
+
info = {
|
|
527
|
+
"Manufacturer": read_sys(base_path + "board_vendor"),
|
|
528
|
+
"Product Name": read_sys(base_path + "board_name"),
|
|
529
|
+
"Version": read_sys(base_path + "board_version"),
|
|
530
|
+
"Serial Number": read_sys(base_path + "board_serial"),
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
for key , value in info.items():
|
|
534
|
+
lines.append(f"{key}: {value if value else 'N/A'}")
|
|
535
|
+
|
|
536
|
+
return lines
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def fan_info():
|
|
542
|
+
lines = ["=== Fan Sensors ==="]
|
|
543
|
+
hwmon_base = "/sys/class/hwmon"
|
|
544
|
+
|
|
545
|
+
if not os.path.exists(hwmon_base):
|
|
546
|
+
return ["Fan sensors not available"]
|
|
547
|
+
|
|
548
|
+
for hw in os.listdir(hwmon_base):
|
|
549
|
+
hw_path = os.path.join(hwmon_base, hw)
|
|
550
|
+
name = read_sys(os.path.join(hw_path, "name")) or hw
|
|
551
|
+
|
|
552
|
+
for file in os.listdir(hw_path):
|
|
553
|
+
if file.startswith("fan") and file.endswith("_input"):
|
|
554
|
+
rpm = read_sys(os.path.join(hw_path, file))
|
|
555
|
+
if rpm and rpm.isdigit():
|
|
556
|
+
lines.append(f"{name} {file}: {rpm} RPM")
|
|
557
|
+
|
|
558
|
+
return lines if len(lines) > 1 else ["==No fans detected==="]
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def partition_info():
|
|
564
|
+
lines = ["=== Partition Information ==="]
|
|
565
|
+
for part in psutil.disk_partitions(all=False):
|
|
566
|
+
lines.append(f"{part.device} mounted on {part.mountpoint} - Type: {part.fstype}")
|
|
567
|
+
return lines
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def drive_info():
|
|
571
|
+
lines = ["=== Drive Information ==="]
|
|
572
|
+
for part in psutil.disk_partitions(all=False):
|
|
573
|
+
try:
|
|
574
|
+
usage = psutil.disk_usage(part.mountpoint)
|
|
575
|
+
except (PermissionError, FileNotFoundError):
|
|
576
|
+
continue
|
|
577
|
+
|
|
578
|
+
lines.append(
|
|
579
|
+
f"{part.mountpoint:15} "
|
|
580
|
+
f"{usage.used // (1024**3):4} / "
|
|
581
|
+
f"{usage.total // (1024**3):4} GB "
|
|
582
|
+
f"({usage.percent:5.1f}%)"
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
return lines
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def gpu_temperature():
|
|
589
|
+
lines = ["=== GPU Temperature ==="]
|
|
590
|
+
temps = psutil.sensors_temperatures()
|
|
591
|
+
if not temps:
|
|
592
|
+
return ["GPU temperature sensors not available"]
|
|
593
|
+
|
|
594
|
+
for name, entries in temps.items():
|
|
595
|
+
for entry in entries:
|
|
596
|
+
if "gpu" in entry.label.lower() or "nvidia" in name.lower() or "amdgpu" in name.lower():
|
|
597
|
+
if entry.label:
|
|
598
|
+
lines.append(f"{entry.label}: {entry.current} °C")
|
|
599
|
+
else:
|
|
600
|
+
lines.append(f"{name}: {entry.current} °C")
|
|
601
|
+
|
|
602
|
+
return lines if len(lines) > 1 else ["===No GPU temperature data found==="]
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def memory_temperature():
|
|
606
|
+
lines = ["=== Memory Temperature ==="]
|
|
607
|
+
temps = psutil.sensors_temperatures()
|
|
608
|
+
if not temps:
|
|
609
|
+
return ["Memory temperature sensors not available"]
|
|
610
|
+
|
|
611
|
+
for name, entries in temps.items():
|
|
612
|
+
for entry in entries:
|
|
613
|
+
if "memory" in entry.label.lower() or "ram" in name.lower():
|
|
614
|
+
if entry.label:
|
|
615
|
+
lines.append(f"{entry.label}: {entry.current} °C")
|
|
616
|
+
else:
|
|
617
|
+
lines.append(f"{name}: {entry.current} °C")
|
|
618
|
+
|
|
619
|
+
return lines if len(lines) > 1 else ["===No Memory temperature data found==="]
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def cpu_temperature():
|
|
623
|
+
lines = ["=== CPU Core Temperatures ==="]
|
|
624
|
+
temps = psutil.sensors_temperatures()
|
|
625
|
+
|
|
626
|
+
if not temps:
|
|
627
|
+
return ["CPU temperature sensors not available"]
|
|
628
|
+
|
|
629
|
+
for name, entries in temps.items():
|
|
630
|
+
if "core" not in name.lower():
|
|
631
|
+
continue
|
|
632
|
+
|
|
633
|
+
for entry in entries:
|
|
634
|
+
if entry.label and "core" in entry.label.lower():
|
|
635
|
+
lines.append(f"{entry.label}: {entry.current} °C")
|
|
636
|
+
|
|
637
|
+
if len(lines) == 1:
|
|
638
|
+
return ["CPU core temperature sensors not found"]
|
|
639
|
+
|
|
640
|
+
return lines
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def keyboard_info():
|
|
644
|
+
lines = ["=== Keyboard Information ==="]
|
|
645
|
+
try:
|
|
646
|
+
if platform.system() == "Linux":
|
|
647
|
+
lsusb_output = os.popen("lsusb | grep -i 'keyboard'").read()
|
|
648
|
+
keyboards = lsusb_output.strip().split("\n")
|
|
649
|
+
if not keyboards or keyboards == ['']:
|
|
650
|
+
return ["===No keyboard information found.==="]
|
|
651
|
+
for kb in keyboards:
|
|
652
|
+
lines.append(kb)
|
|
653
|
+
else:
|
|
654
|
+
lines.append("===Keyboard info not implemented for this OS.===")
|
|
655
|
+
except Exception as e:
|
|
656
|
+
lines.append(f"Keyboard info error: {e}")
|
|
657
|
+
return lines
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def mouse_info():
|
|
661
|
+
lines = ["=== Mouse Information ==="]
|
|
662
|
+
try:
|
|
663
|
+
if platform.system() == "Linux":
|
|
664
|
+
lsusb_output = os.popen("lsusb | grep -i 'mouse'").read()
|
|
665
|
+
mice = lsusb_output.strip().split("\n")
|
|
666
|
+
if not mice or mice == ['']:
|
|
667
|
+
return ["===No mouse information found.==="]
|
|
668
|
+
for mouse in mice:
|
|
669
|
+
lines.append(mouse)
|
|
670
|
+
else:
|
|
671
|
+
lines.append("===Mouse info not implemented for this OS.===")
|
|
672
|
+
except Exception as e:
|
|
673
|
+
lines.append(f"Mouse info error: {e}")
|
|
674
|
+
return lines
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def os_info():
|
|
678
|
+
lines = ["=== Operating System Information ==="]
|
|
679
|
+
try:
|
|
680
|
+
if platform.system() == "Linux":
|
|
681
|
+
distro = ""
|
|
682
|
+
try:
|
|
683
|
+
with open("/etc/os-release") as f:
|
|
684
|
+
for line in f:
|
|
685
|
+
if line.startswith("PRETTY_NAME="):
|
|
686
|
+
distro = line.split("=")[1].strip().strip('"')
|
|
687
|
+
break
|
|
688
|
+
except Exception:
|
|
689
|
+
distro = "Unknown Linux Distro"
|
|
690
|
+
lines.append(f"Distribution: {distro}")
|
|
691
|
+
elif platform.system() == "Windows":
|
|
692
|
+
lines.append(f"Windows Version: {platform.version()}")
|
|
693
|
+
elif platform.system() == "Darwin":
|
|
694
|
+
mac_ver = platform.mac_ver()[0]
|
|
695
|
+
lines.append(f"macOS Version: {mac_ver}")
|
|
696
|
+
else:
|
|
697
|
+
lines.append("OS information not implemented for this OS.")
|
|
698
|
+
except Exception as e:
|
|
699
|
+
lines.append(f"OS info error: {e}")
|
|
700
|
+
return lines
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def wifi_info():
|
|
704
|
+
lines = ["=== Wi-Fi Information ==="]
|
|
705
|
+
try:
|
|
706
|
+
if platform.system() == "Linux":
|
|
707
|
+
iwconfig_output = os.popen("iwconfig 2>/dev/null | grep 'ESSID'").read()
|
|
708
|
+
wifis = iwconfig_output.strip().split("\n")
|
|
709
|
+
if not wifis or wifis == ['']:
|
|
710
|
+
return ["===No Wi-Fi information found.==="]
|
|
711
|
+
for wifi in wifis:
|
|
712
|
+
lines.append(wifi)
|
|
713
|
+
else:
|
|
714
|
+
lines.append("===Wi-Fi info not implemented for this OS.===")
|
|
715
|
+
except Exception as e:
|
|
716
|
+
lines.append(f"Wi-Fi info error: {e}")
|
|
717
|
+
return lines
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
## Print Lines ##
|
|
721
|
+
def main():
|
|
722
|
+
try:
|
|
723
|
+
while True:
|
|
724
|
+
clear_screen()
|
|
725
|
+
# System info
|
|
726
|
+
for line in system_info():
|
|
727
|
+
print(line)
|
|
728
|
+
# Swap memory
|
|
729
|
+
for line in [swap_memory()]:
|
|
730
|
+
print(line)
|
|
731
|
+
# Network
|
|
732
|
+
for line in network_info():
|
|
733
|
+
print(line)
|
|
734
|
+
# Top processes
|
|
735
|
+
for line in top_processes():
|
|
736
|
+
print(line)
|
|
737
|
+
# Drive info
|
|
738
|
+
for line in drive_info():
|
|
739
|
+
print(line)
|
|
740
|
+
# CPU and Memory bar
|
|
741
|
+
for line in [cpu_mem_bar()]:
|
|
742
|
+
print(line)
|
|
743
|
+
# CPU Temperature
|
|
744
|
+
for line in cpu_temperature():
|
|
745
|
+
print(line)
|
|
746
|
+
# Memory Temperature
|
|
747
|
+
for line in memory_temperature():
|
|
748
|
+
print(line)
|
|
749
|
+
# Fan Info
|
|
750
|
+
for line in fan_info():
|
|
751
|
+
print(line)
|
|
752
|
+
# GPU Info
|
|
753
|
+
for line in gpu_info():
|
|
754
|
+
print(line)
|
|
755
|
+
# Motherboard Info
|
|
756
|
+
for line in motherboard_info():
|
|
757
|
+
print(line)
|
|
758
|
+
# GPU Temperature
|
|
759
|
+
for line in gpu_temperature():
|
|
760
|
+
print(line)
|
|
761
|
+
# Battery Info
|
|
762
|
+
for line in [battery_info()]:
|
|
763
|
+
print(f"Battery: {line}")
|
|
764
|
+
# Memory Temperature
|
|
765
|
+
for line in memory_temperature():
|
|
766
|
+
print(line)
|
|
767
|
+
# OS Info
|
|
768
|
+
for line in os_info():
|
|
769
|
+
print(line)
|
|
770
|
+
# Keyboard Info
|
|
771
|
+
for line in keyboard_info():
|
|
772
|
+
print(line)
|
|
773
|
+
# Mouse Info
|
|
774
|
+
for line in mouse_info():
|
|
775
|
+
print(line)
|
|
776
|
+
# Wi-Fi Info
|
|
777
|
+
for line in wifi_info():
|
|
778
|
+
print(line)
|
|
779
|
+
# Partition Info
|
|
780
|
+
for line in partition_info():
|
|
781
|
+
print(line)
|
|
782
|
+
print("\nPress Ctrl+C to exit...")
|
|
783
|
+
time.sleep(1)
|
|
784
|
+
# Intel GPU Info
|
|
785
|
+
for line in intel_gpu_info():
|
|
786
|
+
print(line)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
except KeyboardInterrupt:
|
|
790
|
+
print("\nExiting...")
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
current_theme = "dark"
|
|
794
|
+
|
|
795
|
+
# List of all section functions
|
|
796
|
+
SECTIONS = [
|
|
797
|
+
system_info,
|
|
798
|
+
swap_memory,
|
|
799
|
+
network_info,
|
|
800
|
+
top_processes,
|
|
801
|
+
cpu_mem_bar,
|
|
802
|
+
drive_info,
|
|
803
|
+
fan_info,
|
|
804
|
+
motherboard_info,
|
|
805
|
+
cpu_temperature,
|
|
806
|
+
gpu_info,
|
|
807
|
+
gpu_temperature,
|
|
808
|
+
memory_temperature,
|
|
809
|
+
os_info,
|
|
810
|
+
keyboard_info,
|
|
811
|
+
mouse_info,
|
|
812
|
+
wifi_info,
|
|
813
|
+
partition_info,
|
|
814
|
+
intel_gpu_info,
|
|
815
|
+
|
|
816
|
+
]
|
|
817
|
+
|
|
818
|
+
def apply_theme(root, text, theme_name):
|
|
819
|
+
theme = THEMES[theme_name]
|
|
820
|
+
root.configure(bg=theme["bg"])
|
|
821
|
+
text.configure(bg=theme["bg"], fg=theme["fg"], insertbackground=theme["fg"])
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
# ---------- Summary ---------- #
|
|
825
|
+
def system_summary():
|
|
826
|
+
lines = ["=== SYSTEM SUMMARY ==="]
|
|
827
|
+
|
|
828
|
+
cpu_name = get_cpu_name()
|
|
829
|
+
total_ram = round(psutil.virtual_memory().total / (1024**3), 1)
|
|
830
|
+
disk_total = round(psutil.disk_usage('/').total / (1024**3), 1)
|
|
831
|
+
|
|
832
|
+
# GPU name (simplified)
|
|
833
|
+
gpu_name = "Unknown"
|
|
834
|
+
if platform.system() == "Linux":
|
|
835
|
+
output = os.popen(
|
|
836
|
+
"lspci | grep -Ei '(VGA|3D)' | grep -Ei '(NVIDIA|AMD)'"
|
|
837
|
+
).read().strip()
|
|
838
|
+
if output:
|
|
839
|
+
gpu_name = output.split(":")[-1].strip()
|
|
840
|
+
|
|
841
|
+
lines.append(f"CPU : {cpu_name}")
|
|
842
|
+
lines.append(f"GPU : {gpu_name}")
|
|
843
|
+
lines.append(f"RAM : {total_ram} GB")
|
|
844
|
+
lines.append(f"Disk : {disk_total} GB\n")
|
|
845
|
+
|
|
846
|
+
cpu = psutil.cpu_percent(interval=None)
|
|
847
|
+
mem = psutil.virtual_memory().percent
|
|
848
|
+
disk = psutil.disk_usage('/').percent
|
|
849
|
+
|
|
850
|
+
lines.append(f"CPU Usage : {cpu:.1f}%")
|
|
851
|
+
lines.append(f"Memory Usage: {mem:.1f}%")
|
|
852
|
+
lines.append(f"Disk Usage : {disk:.1f}%")
|
|
853
|
+
|
|
854
|
+
return lines
|
|
855
|
+
|
|
856
|
+
# ---------- Full Sections Placeholder ----------
|
|
857
|
+
# Replace this with your actual SECTIONS list from your app
|
|
858
|
+
SECTIONS = [
|
|
859
|
+
system_info,
|
|
860
|
+
swap_memory,
|
|
861
|
+
network_info,
|
|
862
|
+
top_processes,
|
|
863
|
+
cpu_mem_bar,
|
|
864
|
+
drive_info,
|
|
865
|
+
fan_info,
|
|
866
|
+
motherboard_info,
|
|
867
|
+
cpu_temperature,
|
|
868
|
+
gpu_info,
|
|
869
|
+
gpu_temperature,
|
|
870
|
+
memory_temperature,
|
|
871
|
+
os_info,
|
|
872
|
+
keyboard_info,
|
|
873
|
+
mouse_info,
|
|
874
|
+
wifi_info,
|
|
875
|
+
partition_info,
|
|
876
|
+
intel_gpu_info
|
|
877
|
+
]
|
|
878
|
+
|
|
879
|
+
import time
|
|
880
|
+
|
|
881
|
+
# Global cache for update messages
|
|
882
|
+
_last_update_check = 0
|
|
883
|
+
_update_msg_cache = None
|
|
884
|
+
UPDATE_CHECK_INTERVAL = 600 # seconds (10 minutes)
|
|
885
|
+
|
|
886
|
+
def check_for_updates_cached():
|
|
887
|
+
global _last_update_check, _update_msg_cache
|
|
888
|
+
now = time.time()
|
|
889
|
+
|
|
890
|
+
if now - _last_update_check > UPDATE_CHECK_INTERVAL:
|
|
891
|
+
_last_update_check = now
|
|
892
|
+
_update_msg_cache = check_for_updates() # your existing function
|
|
893
|
+
|
|
894
|
+
return _update_msg_cache
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
# ---------- GUI App ---------- #
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
import tkinter as tk
|
|
902
|
+
def gui_app():
|
|
903
|
+
root = tk.Tk()
|
|
904
|
+
root.title("HardwareMon")
|
|
905
|
+
root.geometry("800x600")
|
|
906
|
+
|
|
907
|
+
summary_mode = tk.BooleanVar(value=True)
|
|
908
|
+
|
|
909
|
+
# ---- Create widgets ---- #
|
|
910
|
+
version_label = tk.Label(root, text=f"HardwareMon v{VERSION}",
|
|
911
|
+
font=("monospace", 12, "bold"))
|
|
912
|
+
version_label.pack(anchor="ne", padx=10, pady=5)
|
|
913
|
+
|
|
914
|
+
text = tk.Text(root, font=("monospace", 11))
|
|
915
|
+
text.pack(fill="both", expand=True, padx=5, pady=5)
|
|
916
|
+
|
|
917
|
+
main_frame = tk.Frame(root)
|
|
918
|
+
main_frame.pack(pady=5, fill="x")
|
|
919
|
+
|
|
920
|
+
toggle_btn = tk.Button(main_frame, text="Show Full Stats")
|
|
921
|
+
toggle_btn.pack(pady=2)
|
|
922
|
+
|
|
923
|
+
cpu_canvas = tk.Canvas(main_frame, width=780, height=100)
|
|
924
|
+
cpu_canvas.pack(pady=2)
|
|
925
|
+
mem_canvas = tk.Canvas(main_frame, width=780, height=100)
|
|
926
|
+
mem_canvas.pack(pady=2)
|
|
927
|
+
disk_canvas = tk.Canvas(main_frame, width=780, height=100)
|
|
928
|
+
disk_canvas.pack(pady=2)
|
|
929
|
+
|
|
930
|
+
# ---- define functions ---- #
|
|
931
|
+
def apply_theme_gui(theme_name):
|
|
932
|
+
theme = THEMES[theme_name]
|
|
933
|
+
root.configure(bg=theme["bg"])
|
|
934
|
+
text.configure(bg=theme["bg"], fg=theme["fg"], insertbackground=theme["fg"])
|
|
935
|
+
version_label.configure(bg=theme["bg"], fg=theme["fg"])
|
|
936
|
+
toggle_btn.configure(bg=theme["bg"], fg=theme["fg"])
|
|
937
|
+
main_frame.configure(bg=theme["bg"])
|
|
938
|
+
cpu_canvas.configure(bg=theme["bg"])
|
|
939
|
+
mem_canvas.configure(bg=theme["bg"])
|
|
940
|
+
disk_canvas.configure(bg=theme["bg"])
|
|
941
|
+
|
|
942
|
+
def refresh_text():
|
|
943
|
+
scroll = text.yview()
|
|
944
|
+
text.delete("1.0", tk.END)
|
|
945
|
+
alerts = check_alerts()
|
|
946
|
+
update_msg = check_for_updates_cached()
|
|
947
|
+
|
|
948
|
+
if alerts:
|
|
949
|
+
version_label.config(text=" | ".join(alerts))
|
|
950
|
+
elif update_msg:
|
|
951
|
+
version_label.config(text=f"HardwareMon v{VERSION} → {update_msg}")
|
|
952
|
+
else:
|
|
953
|
+
version_label.config(text=f"HardwareMon v{VERSION}")
|
|
954
|
+
|
|
955
|
+
active_sections = [system_summary] if summary_mode.get() else SECTIONS
|
|
956
|
+
for section in active_sections:
|
|
957
|
+
lines = section()
|
|
958
|
+
for line in lines:
|
|
959
|
+
text.insert(tk.END, line + "\n")
|
|
960
|
+
text.insert(tk.END, "\n")
|
|
961
|
+
|
|
962
|
+
text.yview_moveto(scroll[0])
|
|
963
|
+
|
|
964
|
+
update_history()
|
|
965
|
+
draw_graph(cpu_canvas, cpu_history, THEMES[current_theme]["accent"], "CPU")
|
|
966
|
+
draw_graph(mem_canvas, mem_history, THEMES[current_theme]["accent"], "Memory")
|
|
967
|
+
draw_graph(disk_canvas, disk_history, THEMES[current_theme]["accent"], "Disk")
|
|
968
|
+
|
|
969
|
+
def toggle_view():
|
|
970
|
+
summary_mode.set(not summary_mode.get())
|
|
971
|
+
toggle_btn.config(text="Show Full Stats" if summary_mode.get() else "Show Summary")
|
|
972
|
+
refresh_text()
|
|
973
|
+
|
|
974
|
+
toggle_btn.config(command=toggle_view)
|
|
975
|
+
|
|
976
|
+
def switch_theme(event=None):
|
|
977
|
+
global current_theme
|
|
978
|
+
theme_names = list(THEMES.keys())
|
|
979
|
+
next_index = (theme_names.index(current_theme) + 1) % len(theme_names)
|
|
980
|
+
current_theme = theme_names[next_index]
|
|
981
|
+
apply_theme_gui(current_theme)
|
|
982
|
+
refresh_text()
|
|
983
|
+
|
|
984
|
+
root.bind("<F2>", lambda e: toggle_view())
|
|
985
|
+
root.bind("<F3>", switch_theme)
|
|
986
|
+
|
|
987
|
+
apply_theme_gui(current_theme)
|
|
988
|
+
refresh_text()
|
|
989
|
+
|
|
990
|
+
def update_loop():
|
|
991
|
+
refresh_text()
|
|
992
|
+
root.after(1000, update_loop)
|
|
993
|
+
|
|
994
|
+
update_loop()
|
|
995
|
+
root.mainloop()
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
if __name__ == "__main__":
|
|
1003
|
+
check_for_updates()
|
|
1004
|
+
gui_app()
|
|
1005
|
+
|
|
1006
|
+
## -- END GUI APP -- ##
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
## Made with <3 by Louis ##
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65.5.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hardwaremon"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Hardware monitoring tool for CPU, RAM, and battery."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{name="Louis Hinchliffe", email="your.email@example.com"}]
|
|
11
|
+
license = {file="LICENSE"}
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"psutil",
|
|
15
|
+
"requests",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
hardwaremon = "hardwaremon.HardwareMon:main" # if you have a main() function
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="HardwareMon",
|
|
5
|
+
version="2.0.0",
|
|
6
|
+
packages=find_packages(exclude=["test_hardwaremon*"]),
|
|
7
|
+
install_requires=[
|
|
8
|
+
"psutil",
|
|
9
|
+
"requests",
|
|
10
|
+
# add any other dependencies your project needs
|
|
11
|
+
],
|
|
12
|
+
entry_points={
|
|
13
|
+
"console_scripts": [
|
|
14
|
+
"hardwaremon=HardwareMon.main:main", # adjust if your main file is different
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
)
|