Rename to entropymon, restructure as PyPI package
- Renamed systats -> entropymon everywhere (code, logo, config paths)
- New ASCII art logo: ENTROPY MON
- Proper Python package structure: entropymon/{__init__,__main__,monitor}.py
- pyproject.toml with entry_points (pip install -> entropymon command)
- MIT license, README with full docs
- Works: pip install, entropymon CLI, python -m entropymon
- Optional nvidia extra: pip install entropymon[nvidia]
- --version flag added
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.eggs/
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Electric Entropy Lab
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# entropymon
|
||||
|
||||
**Terminal system monitor by Electric Entropy Lab**
|
||||
|
||||
A powerful, feature-rich terminal system monitor built with Python curses. Better than glances, prettier than htop.
|
||||
|
||||
## Features
|
||||
|
||||
- **CPU** - per-core usage bars, sparkline history, frequency, load average
|
||||
- **Memory** - RAM/Swap with usage bars
|
||||
- **Disk** - partitions with usage, IO rates (read/write)
|
||||
- **Network** - RX/TX rates with sparklines, per-interface info
|
||||
- **GPU** - NVIDIA (via NVML) and AMD (via sysfs) - utilization, VRAM, temp, power, fan, clocks
|
||||
- **Temperatures** - CPU, GPU, NVMe, network adapters
|
||||
- **Processes** - interactive management with sorting, filtering, kill signals, renice, details
|
||||
- **Multi-language** - EN, PL, DE, ES, FR, UK, ZH with runtime switching
|
||||
- **Animated intro** - matrix rain boot sequence with decrypt logo animation
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install entropymon
|
||||
```
|
||||
|
||||
With NVIDIA GPU support:
|
||||
|
||||
```bash
|
||||
pip install entropymon[nvidia]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
entropymon # start with auto-detected language
|
||||
entropymon --lang pl # start in Polish
|
||||
entropymon --no-intro # skip boot animation
|
||||
entropymon --reset-lang # reset saved language preference
|
||||
entropymon --version # show version
|
||||
```
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `h` / `F1` / `?` | Help overlay |
|
||||
| `L` | Switch language |
|
||||
| `c` / `m` / `p` / `n` | Sort by CPU/MEM/PID/Name |
|
||||
| `r` | Reverse sort |
|
||||
| `j` / `k` / arrows | Navigate processes |
|
||||
| `T` | Send SIGTERM |
|
||||
| `K` | Send SIGKILL |
|
||||
| `F9` | Signal selection menu |
|
||||
| `+` / `-` | Renice process |
|
||||
| `d` / `F5` / Enter | Process details |
|
||||
| `/` / `f` | Filter processes |
|
||||
| `q` / `Esc` | Quit |
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- Linux (uses `/proc`, `/sys`)
|
||||
- psutil
|
||||
- pynvml (optional, for NVIDIA GPU)
|
||||
|
||||
## License
|
||||
|
||||
MIT - Electric Entropy Lab
|
||||
3
entropymon/__init__.py
Normal file
3
entropymon/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""entropymon - Terminal system monitor by Electric Entropy Lab."""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
5
entropymon/__main__.py
Normal file
5
entropymon/__main__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Allow running as: python -m entropymon"""
|
||||
from entropymon.monitor import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""systats - Terminal system monitor (better than glances)."""
|
||||
"""entropymon - Terminal system monitor by Electric Entropy Lab."""
|
||||
|
||||
import curses
|
||||
import os
|
||||
@@ -91,7 +91,7 @@ TRANSLATIONS = {
|
||||
"set_nice": {"en": "Set nice {val} for {name} (PID {pid})", "pl": "Ustawiono priorytet {val} dla {name} (PID {pid})", "de": "Nice {val} fuer {name} gesetzt (PID {pid})", "es": "Nice {val} para {name} (PID {pid})", "fr": "Nice {val} pour {name} (PID {pid})", "uk": "Vstanovleno priorytet {val} dlya {name} (PID {pid})", "zh": "Yi shezhi nice {val} {name} (PID {pid})"},
|
||||
"invalid_nice": {"en": "Invalid nice value", "pl": "Nieprawidlowy priorytet", "de": "Ungueltiger Nice-Wert", "es": "Valor nice invalido", "fr": "Valeur nice invalide", "uk": "Nepravyl'nyj priorytet", "zh": "Wuxiao nice zhi"},
|
||||
# Help
|
||||
"help_title": {"en": "SYSTATS - Keyboard Shortcuts", "pl": "SYSTATS - Skroty klawiszowe", "de": "SYSTATS - Tastenkuerzel", "es": "SYSTATS - Atajos de teclado", "fr": "SYSTATS - Raccourcis clavier", "uk": "SYSTATS - Klavishni skroty", "zh": "SYSTATS - Jianpan kuaijiejian"},
|
||||
"help_title": {"en": "ENTROPYMON - Keyboard Shortcuts", "pl": "ENTROPYMON - Skroty klawiszowe", "de": "ENTROPYMON - Tastenkuerzel", "es": "ENTROPYMON - Atajos de teclado", "fr": "ENTROPYMON - Raccourcis clavier", "uk": "ENTROPYMON - Klavishni skroty", "zh": "ENTROPYMON - Jianpan kuaijiejian"},
|
||||
"help_close": {"en": "Press h, F1 or ? to close", "pl": "Nacisnij h, F1 lub ? aby zamknac", "de": "h, F1 oder ? zum Schliessen", "es": "Pulsa h, F1 o ? para cerrar", "fr": "Appuyez h, F1 ou ? pour fermer", "uk": "Natysnit' h, F1 abo ? shchob zakryty", "zh": "An h, F1 huo ? guanbi"},
|
||||
"help_nav": {"en": "NAVIGATION", "pl": "NAWIGACJA", "de": "NAVIGATION", "es": "NAVEGACION", "fr": "NAVIGATION", "uk": "NAVIGACIYA", "zh": "DAOHANG"},
|
||||
"help_down": {"en": "Move cursor down", "pl": "Kursor w dol", "de": "Cursor nach unten", "es": "Mover cursor abajo", "fr": "Curseur vers le bas", "uk": "Kursor vnyz", "zh": "Guangbiao xia yi"},
|
||||
@@ -769,7 +769,7 @@ class Renderer:
|
||||
d = self.data
|
||||
hostname = os.uname().nodename
|
||||
kernel = os.uname().release
|
||||
header = f" SYSTATS | {hostname} | Linux {kernel} | {t('up')}: {d.uptime} "
|
||||
header = f" ENTROPYMON | {hostname} | Linux {kernel} | {t('up')}: {d.uptime} "
|
||||
header += f"| {t('load')}: {d.load_avg[0]:.2f} {d.load_avg[1]:.2f} {d.load_avg[2]:.2f} "
|
||||
header += f"| {t('procs')}: {d.proc_count} ({d.proc_running} {t('run')}) "
|
||||
brand = " Electric Entropy Lab "
|
||||
@@ -1219,23 +1219,29 @@ class Renderer:
|
||||
VERSION = "1.0.0"
|
||||
|
||||
LOGO_ART = [
|
||||
" ███████╗██╗ ██╗███████╗████████╗ █████╗ ████████╗███████╗",
|
||||
" ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██╔════╝",
|
||||
" ███████╗ ╚████╔╝ ███████╗ ██║ ███████║ ██║ ███████╗",
|
||||
" ╚════██║ ╚██╔╝ ╚════██║ ██║ ██╔══██║ ██║ ╚════██║",
|
||||
" ███████║ ██║ ███████║ ██║ ██║ ██║ ██║ ███████║",
|
||||
" ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝",
|
||||
" ███████╗███╗ ██╗████████╗██████╗ ██████╗ ██████╗ ██╗ ██╗",
|
||||
" ██╔════╝████╗ ██║╚══██╔══╝██╔══██╗██╔═══██╗██╔══██╗╚██╗ ██╔╝",
|
||||
" █████╗ ██╔██╗ ██║ ██║ ██████╔╝██║ ██║██████╔╝ ╚████╔╝ ",
|
||||
" ██╔══╝ ██║╚██╗██║ ██║ ██╔══██╗██║ ██║██╔═══╝ ╚██╔╝ ",
|
||||
" ███████╗██║ ╚████║ ██║ ██║ ██║╚██████╔╝██║ ██║ ",
|
||||
" ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ",
|
||||
" ███╗ ███╗ ██████╗ ███╗ ██╗ ",
|
||||
" ████╗ ████║██╔═══██╗████╗ ██║ ",
|
||||
" ██╔████╔██║██║ ██║██╔██╗ ██║ ",
|
||||
" ██║╚██╔╝██║██║ ██║██║╚██╗██║ ",
|
||||
" ██║ ╚═╝ ██║╚██████╔╝██║ ╚████║ ",
|
||||
" ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ",
|
||||
]
|
||||
|
||||
BOOT_LINES = [
|
||||
("> SYSTATS v{ver}", 0.0),
|
||||
("> ENTROPYMON v{ver}", 0.0),
|
||||
("> initializing system probes... OK", 0.3),
|
||||
("> scanning CPU cores... {cores} detected", 0.6),
|
||||
("> GPU subsystem... {gpu}", 0.9),
|
||||
("> network interfaces... ONLINE", 1.1),
|
||||
("> thermal sensors... {temps}", 1.3),
|
||||
("> process table... READY", 1.5),
|
||||
("> Electric Entropy Lab // SYSTATS", 1.8),
|
||||
("> Electric Entropy Lab // ENTROPYMON", 1.8),
|
||||
]
|
||||
|
||||
import random as _random
|
||||
@@ -1298,7 +1304,7 @@ def intro_splash(stdscr):
|
||||
|
||||
# ── Phase 2: Boot sequence with typing effect ──
|
||||
boot_lines = [
|
||||
(f"> SYSTATS v{VERSION} // Electric Entropy Lab", C_ACCENT),
|
||||
(f"> ENTROPYMON v{VERSION} // Electric Entropy Lab", C_ACCENT),
|
||||
(f"> host: {hostname}", C_DIM),
|
||||
(f"> scanning CPU... {cores_phys}C/{cores}T detected", C_LOW),
|
||||
(f"> memory probe... {mem_str} OK", C_LOW),
|
||||
@@ -1514,7 +1520,7 @@ def intro_splash(stdscr):
|
||||
show_intro = True
|
||||
|
||||
|
||||
def main(stdscr):
|
||||
def _curses_main(stdscr):
|
||||
if show_intro:
|
||||
intro_splash(stdscr)
|
||||
|
||||
@@ -1673,7 +1679,7 @@ def main(stdscr):
|
||||
|
||||
|
||||
def get_config_path():
|
||||
config_dir = os.path.join(os.path.expanduser("~"), ".config", "systats")
|
||||
config_dir = os.path.join(os.path.expanduser("~"), ".config", "entropymon")
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
return os.path.join(config_dir, "config")
|
||||
|
||||
@@ -1700,7 +1706,7 @@ def save_lang(lang):
|
||||
|
||||
def ask_lang():
|
||||
print(" \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")
|
||||
print(" \u2502 SYSTATS - Electric Entropy Lab \u2502")
|
||||
print(" \u2502 ENTROPYMON - Electric Entropy Lab \u2502")
|
||||
print(" \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524")
|
||||
print(" \u2502 Choose your language: \u2502")
|
||||
print(" \u2502 \u2502")
|
||||
@@ -1723,9 +1729,13 @@ def ask_lang():
|
||||
print(f" 1-{len(LANGS)} or lang code ({'/'.join(LANGS)})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
"""CLI entry point for entropymon."""
|
||||
global current_lang, show_intro
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="systats - Terminal system monitor | Electric Entropy Lab")
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="entropymon",
|
||||
description="entropymon - Terminal system monitor | Electric Entropy Lab")
|
||||
lang_help = ", ".join(f"{c} ({n})" for c, n in LANG_NAMES.items())
|
||||
parser.add_argument("--lang", choices=LANGS, default=None,
|
||||
help=f"UI language: {lang_help}")
|
||||
@@ -1733,6 +1743,7 @@ if __name__ == "__main__":
|
||||
help="Reset saved language preference and ask again")
|
||||
parser.add_argument("--no-intro", action="store_true",
|
||||
help="Skip the boot intro animation")
|
||||
parser.add_argument("--version", action="version", version=f"entropymon {VERSION}")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.reset_lang:
|
||||
@@ -1765,6 +1776,10 @@ if __name__ == "__main__":
|
||||
show_intro = False
|
||||
|
||||
try:
|
||||
curses.wrapper(main)
|
||||
curses.wrapper(_curses_main)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
52
pyproject.toml
Normal file
52
pyproject.toml
Normal file
@@ -0,0 +1,52 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=68.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "entropymon"
|
||||
dynamic = ["version"]
|
||||
description = "Terminal system monitor with GPU support, process management, and multi-language UI"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
requires-python = ">=3.8"
|
||||
authors = [
|
||||
{name = "Electric Entropy Lab", email = "patryk@electricentropy.eu"},
|
||||
]
|
||||
keywords = ["monitor", "system", "terminal", "tui", "gpu", "nvidia", "amd", "htop", "glances", "curses"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console :: Curses",
|
||||
"Intended Audience :: System Administrators",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: System :: Monitoring",
|
||||
"Topic :: Utilities",
|
||||
]
|
||||
dependencies = [
|
||||
"psutil>=5.9.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
nvidia = ["pynvml>=11.0.0"]
|
||||
all = ["pynvml>=11.0.0"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.electricentropy.eu/ar3dh3l/systats"
|
||||
Repository = "https://git.electricentropy.eu/ar3dh3l/systats"
|
||||
Issues = "https://git.electricentropy.eu/ar3dh3l/systats/issues"
|
||||
|
||||
[project.scripts]
|
||||
entropymon = "entropymon.monitor:main"
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "entropymon.__version__"}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["entropymon*"]
|
||||
Reference in New Issue
Block a user