msiltop 0.2.1__tar.gz → 0.2.3__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.
@@ -81,7 +81,7 @@ pip install msiltop
81
81
  sudo uv run msiltop
82
82
 
83
83
  # Run with options
84
- sudo uv run msiltop --interval 2 --color 5 --avg 60 --show_cores
84
+ sudo uv run msiltop --interval 2 --theme cyan --avg 60 --show_cores
85
85
 
86
86
  # Alternative: run the module directly
87
87
  sudo uv run -m msiltop.msiltop
@@ -99,12 +99,13 @@ sudo msiltop
99
99
  msiltop
100
100
 
101
101
  # With options
102
- msiltop --interval 2 --color 5 --avg 60 --show_cores
102
+ msiltop --interval 2 --theme cyan --avg 60 --show_cores
103
103
  ```
104
104
 
105
105
  ### Available Command Line Options
106
106
  - `--interval INTERVAL`: Display and powermetrics sampling interval (seconds, default: 1)
107
- - `--color COLOR`: Color theme selection 0-8 (default: 2)
107
+ - `--theme THEME`: Color theme selection (default: cyan)
108
+ - Options: default, dark, blue, green, red, purple, orange, cyan, magenta
108
109
  - `--avg AVG`: Averaging window for power values (seconds, default: 30)
109
110
  - `--show_cores`: Enable individual core monitoring display
110
111
  - `--max_count COUNT`: Restart powermetrics after N samples (for long-running sessions)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msiltop
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Real-time macOS hardware performance monitoring for Apple Silicon (M1/M2/M3/M4) with AI workload focus - enhanced asitop alternative
5
5
  Project-URL: Homepage, https://github.com/pratikdevnani/msiltop
6
6
  Project-URL: Repository, https://github.com/pratikdevnani/msiltop
@@ -63,7 +63,7 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
63
63
  sudo uvx msiltop
64
64
 
65
65
  # Run with custom options
66
- sudo uvx msiltop --interval 2 --color 5 --avg 60
66
+ sudo uvx msiltop --interval 2 --theme cyan --avg 60
67
67
 
68
68
  # Or install as a tool for regular usages
69
69
  uv tool install msiltop@latest -U
@@ -77,7 +77,7 @@ uv tool install msiltop@latest -U
77
77
 
78
78
  ```bash
79
79
  # Run with custom options
80
- sudo uvx msiltop --interval 2 --color 5 --avg 60
80
+ sudo uvx msiltop --interval 2 --theme cyan --avg 60
81
81
  ```
82
82
 
83
83
  ### Install from PyPI
@@ -131,7 +131,8 @@ msiltop [OPTIONS]
131
131
 
132
132
  Options:
133
133
  --interval INTERVAL Display refresh rate in seconds (default: 1.0)
134
- --color COLOR Color theme selection 0-8 (default: 2)
134
+ --theme THEME Color theme selection (default: cyan)
135
+ Options: default, dark, blue, green, red, purple, orange, cyan, magenta
135
136
  --avg AVG Power averaging window in seconds (default: 30)
136
137
  --show_cores Enable individual CPU core monitoring
137
138
  --max_count COUNT Restart powermetrics after N samples (stability)
@@ -150,8 +151,8 @@ sudo msiltop --interval 0.5 --show_cores
150
151
  # Long-term monitoring with 60-second power averaging
151
152
  sudo msiltop --avg 60 --max_count 1000
152
153
 
153
- # Custom color theme
154
- sudo msiltop --color 5
154
+ # Custom theme
155
+ sudo msiltop --theme purple
155
156
  ```
156
157
 
157
158
  ## 🔧 How MSILTOP Works
@@ -24,7 +24,7 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
24
24
  sudo uvx msiltop
25
25
 
26
26
  # Run with custom options
27
- sudo uvx msiltop --interval 2 --color 5 --avg 60
27
+ sudo uvx msiltop --interval 2 --theme cyan --avg 60
28
28
 
29
29
  # Or install as a tool for regular usages
30
30
  uv tool install msiltop@latest -U
@@ -38,7 +38,7 @@ uv tool install msiltop@latest -U
38
38
 
39
39
  ```bash
40
40
  # Run with custom options
41
- sudo uvx msiltop --interval 2 --color 5 --avg 60
41
+ sudo uvx msiltop --interval 2 --theme cyan --avg 60
42
42
  ```
43
43
 
44
44
  ### Install from PyPI
@@ -92,7 +92,8 @@ msiltop [OPTIONS]
92
92
 
93
93
  Options:
94
94
  --interval INTERVAL Display refresh rate in seconds (default: 1.0)
95
- --color COLOR Color theme selection 0-8 (default: 2)
95
+ --theme THEME Color theme selection (default: cyan)
96
+ Options: default, dark, blue, green, red, purple, orange, cyan, magenta
96
97
  --avg AVG Power averaging window in seconds (default: 30)
97
98
  --show_cores Enable individual CPU core monitoring
98
99
  --max_count COUNT Restart powermetrics after N samples (stability)
@@ -111,8 +112,8 @@ sudo msiltop --interval 0.5 --show_cores
111
112
  # Long-term monitoring with 60-second power averaging
112
113
  sudo msiltop --avg 60 --max_count 1000
113
114
 
114
- # Custom color theme
115
- sudo msiltop --color 5
115
+ # Custom theme
116
+ sudo msiltop --theme purple
116
117
  ```
117
118
 
118
119
  ## 🔧 How MSILTOP Works
@@ -86,7 +86,7 @@ sudo msiltop
86
86
 
87
87
  Try these solutions:
88
88
  1. Resize your terminal window to be larger
89
- 2. Use a different color theme: `sudo msiltop --color 0`
89
+ 2. Use a different theme: `sudo msiltop --theme default`
90
90
  3. Clear your terminal: `clear` before running FluidTop
91
91
  4. Update to the latest version: `uv tool install msiltop@latest -U`
92
92
 
@@ -1,6 +1,7 @@
1
1
  import time
2
2
  import click
3
3
  import asyncio
4
+ import json
4
5
  from collections import deque
5
6
  from typing import Optional
6
7
 
@@ -410,7 +411,7 @@ class FluidTopApp(App):
410
411
 
411
412
  # CSS is set dynamically in _apply_theme method
412
413
 
413
- def __init__(self, interval: int, theme: str, avg: int, max_count: int, show_cores: bool = False):
414
+ def __init__(self, interval: int, theme: str, avg: int, max_count: int, show_cores: bool = False, thermal_bell: bool = False):
414
415
  self.interval = interval
415
416
  # Store theme temporarily, don't assign to self.theme yet
416
417
  theme_value = theme
@@ -425,6 +426,7 @@ class FluidTopApp(App):
425
426
  self.avg = avg
426
427
  self.max_count = max_count
427
428
  self.show_cores = show_cores
429
+ self.thermal_bell = thermal_bell
428
430
 
429
431
  # Initialize metrics storage
430
432
  # No longer tracking averages or peaks
@@ -440,6 +442,19 @@ class FluidTopApp(App):
440
442
  self.timecode = None
441
443
  self.last_timestamp = 0
442
444
  self.count = 0
445
+ self.last_thermal_pressure = None
446
+ self.session_start = time.time()
447
+ self.summary_samples = 0
448
+ self.cpu_usage_sum = 0.0
449
+ self.gpu_usage_sum = 0.0
450
+ self.ram_usage_sum = 0.0
451
+ self.cpu_usage_peak = 0.0
452
+ self.gpu_usage_peak = 0.0
453
+ self.ram_usage_peak = 0.0
454
+ self.cpu_power_peak = 0.0
455
+ self.gpu_power_peak = 0.0
456
+ self.ane_power_peak = 0.0
457
+ self.thermal_pressure_max = "Nominal"
443
458
 
444
459
  # SoC info
445
460
  self.soc_info_dict = get_soc_info()
@@ -587,6 +602,13 @@ class FluidTopApp(App):
587
602
  height: 100%;
588
603
  }}
589
604
 
605
+ #thermal-label {{
606
+ width: auto;
607
+ text-align: right;
608
+ color: $text;
609
+ padding: 0 1;
610
+ }}
611
+
590
612
  #timestamp-label {{
591
613
  width: auto;
592
614
  text-align: right;
@@ -617,6 +639,7 @@ class FluidTopApp(App):
617
639
  yield Label("Initializing...", id="system-info-label")
618
640
  # Timestamp on the right
619
641
  with Horizontal(id="controls-buttons"):
642
+ yield Label("Thermal Pressure: --", id="thermal-label")
620
643
  yield Label("", id="timestamp-label")
621
644
 
622
645
  # Usage Charts section
@@ -752,6 +775,7 @@ class FluidTopApp(App):
752
775
  # Update title to show both CPU types (smoothed values)
753
776
  combined_title = f"E-CPU: {e_cpu_usage}% | P-CPU: {p_cpu_usage}%"
754
777
  cpu_combined_chart.update_title(combined_title)
778
+ combined_cpu_usage = (e_cpu_usage + p_cpu_usage) / 2
755
779
 
756
780
  # Update GPU usage chart
757
781
  gpu_chart = self.query_one("#gpu-usage-chart", UsageChart)
@@ -763,7 +787,7 @@ class FluidTopApp(App):
763
787
  # Update RAM usage chart with swap information
764
788
  ram_metrics_dict = get_ram_metrics_dict()
765
789
  ram_chart = self.query_one("#ram-usage-chart", UsageChart)
766
- ram_usage_percent = 100 - ram_metrics_dict["free_percent"] # Convert from free to used percentage
790
+ ram_usage_percent = ram_metrics_dict["used_percent"]
767
791
 
768
792
  # Include swap information in the title
769
793
  if ram_metrics_dict["swap_total_GB"] < 0.1:
@@ -774,6 +798,14 @@ class FluidTopApp(App):
774
798
  ram_chart.update_title(ram_title)
775
799
  ram_chart.add_data(ram_usage_percent, render=should_render)
776
800
 
801
+ self.summary_samples += 1
802
+ self.cpu_usage_sum += combined_cpu_usage
803
+ self.gpu_usage_sum += gpu_usage
804
+ self.ram_usage_sum += ram_usage_percent
805
+ self.cpu_usage_peak = max(self.cpu_usage_peak, combined_cpu_usage)
806
+ self.gpu_usage_peak = max(self.gpu_usage_peak, gpu_usage)
807
+ self.ram_usage_peak = max(self.ram_usage_peak, ram_usage_percent)
808
+
777
809
  if self.show_cores:
778
810
  await self.update_core_usage_charts(cpu_metrics_dict, should_render)
779
811
 
@@ -817,6 +849,10 @@ class FluidTopApp(App):
817
849
  cpu_power_W = cpu_metrics_dict["cpu_W"]
818
850
  gpu_power_W = cpu_metrics_dict["gpu_W"]
819
851
  ane_power_W = cpu_metrics_dict["ane_W"]
852
+
853
+ self.cpu_power_peak = max(self.cpu_power_peak, cpu_power_W)
854
+ self.gpu_power_peak = max(self.gpu_power_peak, gpu_power_W)
855
+ self.ane_power_peak = max(self.ane_power_peak, ane_power_W)
820
856
 
821
857
  # Update energy consumption for each component (watts * seconds = watt-seconds)
822
858
  self.total_energy_consumed += package_power_W * self.interval
@@ -866,10 +902,19 @@ class FluidTopApp(App):
866
902
  ane_power_chart.add_data(ane_power_percent, render=should_render)
867
903
 
868
904
  # Update system info label with total power and thermal info
869
- thermal_throttle = "no" if thermal_pressure == "Nominal" else "yes"
870
905
  total_energy_display = format_energy(self.total_energy_consumed)
871
- system_info = f"{self.soc_info_dict['name']} ({self.soc_info_dict['e_core_count']}E+{self.soc_info_dict['p_core_count']}P+{self.soc_info_dict['gpu_core_count']}GPU) | Total: {package_power_W:.1f}W ({total_energy_display}) | Throttle: {thermal_throttle}"
906
+ system_info = f"{self.soc_info_dict['name']} ({self.soc_info_dict['e_core_count']}E+{self.soc_info_dict['p_core_count']}P+{self.soc_info_dict['gpu_core_count']}GPU) | Total: {package_power_W:.1f}W ({total_energy_display})"
872
907
  self.query_one("#system-info-label", Label).update(system_info)
908
+ self.query_one("#thermal-label", Label).update(f"Thermal Pressure: {thermal_pressure}")
909
+ thermal_levels = ["Nominal", "Moderate", "Heavy", "Critical"]
910
+ if thermal_pressure in thermal_levels and thermal_levels.index(thermal_pressure) > thermal_levels.index(self.thermal_pressure_max):
911
+ self.thermal_pressure_max = thermal_pressure
912
+ if self.thermal_bell and thermal_pressure != "Nominal" and thermal_pressure != self.last_thermal_pressure:
913
+ try:
914
+ print("\a", end="", flush=True)
915
+ except Exception:
916
+ pass
917
+ self.last_thermal_pressure = thermal_pressure
873
918
 
874
919
  async def update_timestamp(self):
875
920
  """Update the timestamp display"""
@@ -885,6 +930,28 @@ class FluidTopApp(App):
885
930
  self.powermetrics_process.terminate()
886
931
  except:
887
932
  pass
933
+ duration_sec = max(0, time.time() - self.session_start)
934
+ if self.summary_samples > 0:
935
+ avg_cpu = self.cpu_usage_sum / self.summary_samples
936
+ avg_gpu = self.gpu_usage_sum / self.summary_samples
937
+ avg_ram = self.ram_usage_sum / self.summary_samples
938
+ else:
939
+ avg_cpu = avg_gpu = avg_ram = 0.0
940
+ summary = {
941
+ "duration_sec": round(duration_sec, 1),
942
+ "avg_cpu_percent": round(avg_cpu, 2),
943
+ "avg_gpu_percent": round(avg_gpu, 2),
944
+ "avg_ram_percent": round(avg_ram, 2),
945
+ "peak_cpu_percent": round(self.cpu_usage_peak, 2),
946
+ "peak_gpu_percent": round(self.gpu_usage_peak, 2),
947
+ "peak_ram_percent": round(self.ram_usage_peak, 2),
948
+ "peak_cpu_w": round(self.cpu_power_peak, 2),
949
+ "peak_gpu_w": round(self.gpu_power_peak, 2),
950
+ "peak_ane_w": round(self.ane_power_peak, 2),
951
+ "total_energy_wh": round(self.total_energy_consumed / 3600, 3),
952
+ "thermal_pressure_max": self.thermal_pressure_max,
953
+ }
954
+ print("\nSession summary:\n" + json.dumps(summary, indent=2))
888
955
 
889
956
  @click.command()
890
957
  @click.option('--interval', type=float, default=1.0,
@@ -897,19 +964,21 @@ class FluidTopApp(App):
897
964
  help='Max show count to restart powermetrics')
898
965
  @click.option('--show_cores', is_flag=True, default=False,
899
966
  help='Show per-core CPU usage charts')
900
- def main(interval, theme, avg, max_count, show_cores):
967
+ @click.option('--thermal_bell', is_flag=True, default=False,
968
+ help='Ring terminal bell when thermal throttling is detected')
969
+ def main(interval, theme, avg, max_count, show_cores, thermal_bell):
901
970
  """msiltop: Performance monitoring CLI tool for Apple Silicon"""
902
- return _main_logic(interval, theme, avg, max_count, show_cores=show_cores)
971
+ return _main_logic(interval, theme, avg, max_count, show_cores=show_cores, thermal_bell=thermal_bell)
903
972
 
904
973
 
905
- def _main_logic(interval, theme, avg, max_count, show_cores=False):
974
+ def _main_logic(interval, theme, avg, max_count, show_cores=False, thermal_bell=False):
906
975
  """Main logic using Textual app"""
907
976
  print("\nMSILTOP - Performance monitoring CLI tool for Apple Silicon")
908
977
  print("Get help at `https://github.com/pratikdevnani/msiltop`")
909
978
  print("P.S. You are recommended to run MSILTOP with `sudo msiltop`\n")
910
979
 
911
980
  # Create and run the Textual app
912
- app = FluidTopApp(interval, theme, avg, max_count, show_cores=show_cores)
981
+ app = FluidTopApp(interval, theme, avg, max_count, show_cores=show_cores, thermal_bell=thermal_bell)
913
982
  try:
914
983
  app.run()
915
984
  except KeyboardInterrupt:
@@ -81,7 +81,7 @@ def get_ram_metrics_dict():
81
81
  "total_GB": round(total_GB, 1),
82
82
  "free_GB": round(free_GB, 1),
83
83
  "used_GB": round(used_GB, 1),
84
- "free_percent": int(100-(ram_metrics.available/ram_metrics.total*100)),
84
+ "used_percent": int(ram_metrics.percent),
85
85
  "swap_total_GB": swap_total_GB,
86
86
  "swap_used_GB": swap_used_GB,
87
87
  "swap_free_GB": swap_free_GB,
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "msiltop"
7
- version = "0.2.1"
7
+ version = "0.2.3"
8
8
  description = "Real-time macOS hardware performance monitoring for Apple Silicon (M1/M2/M3/M4) with AI workload focus - enhanced asitop alternative"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -78,7 +78,7 @@ wheels = [
78
78
 
79
79
  [[package]]
80
80
  name = "msiltop"
81
- version = "0.1.0"
81
+ version = "0.2.2"
82
82
  source = { editable = "." }
83
83
  dependencies = [
84
84
  { name = "click" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes