amdgpu_fan 0.3.0 → 0.7.0

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.
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../config/environment'
4
-
5
- # The main class
6
- class AmdgpuFanCli < Thor
7
- desc 'auto', 'Set fan mode to automatic (requires sudo)'
8
- def auto
9
- amdgpu_service.set_fan_mode! :auto
10
- puts fan_status
11
- end
12
-
13
- desc 'connectors', 'View the status of the display connectors'
14
- def connectors
15
- amdgpu_service.connectors_status.each do |connector,status|
16
- puts "#{connector}: #{status}"
17
- end
18
- end
19
-
20
- desc 'set PERCENTAGE', 'Set fan speed to PERCENTAGE (requires sudo)'
21
- def set(percentage)
22
- return puts "Invalid percentage" unless (0..100).cover?(percentage.to_i)
23
-
24
- amdgpu_service.set_fan_manual_speed! percent: percentage
25
- puts fan_status
26
- rescue AmdgpuService::Error
27
- puts 'Invalid fan speed provided. The percentage should be between 1 and 100'
28
- exit 1
29
- end
30
-
31
- desc 'status', 'View device info, current fan speed, and temperature'
32
- def status
33
- puts radeon_logo,
34
- "📺\tGPU: #{amdgpu_service.name}",
35
- "📄\tvBIOS: #{amdgpu_service.vbios_version}",
36
- clock_status,
37
- fan_status,
38
- "🌡\tTemp: #{amdgpu_service.temperature}°C",
39
- "⚡\tPower: #{amdgpu_service.power_dpm_state} mode using " \
40
- "#{amdgpu_service.power_draw} / #{amdgpu_service.power_max} Watts",
41
- "⚖\tLoad: #{percent_meter amdgpu_service.busy_percent, 20}"
42
- end
43
-
44
- desc 'watch [SECONDS]', 'Watch fan speed, load, power, and temperature ' \
45
- 'refreshed every n seconds'
46
- def watch(seconds=1)
47
- return puts "Seconds must be from 1 to 600" unless (1..600).cover?(seconds.to_i)
48
-
49
- puts "Watching #{amdgpu_service.name} every #{seconds} second(s)...",
50
- ' <Press Ctrl-C to exit>'
51
-
52
- trap "SIGINT" do
53
- puts "\nAnd now the watch is ended."
54
- exit 0
55
- end
56
-
57
- loop do
58
- puts "#{Time.now.strftime("%F %T")} - " \
59
- "Clock: #{amdgpu_service.core_clock} Core, #{amdgpu_service.memory_clock} Memory,\t" \
60
- "Fan: #{amdgpu_service.fan_speed_rpm} rpm #{percent_meter amdgpu_service.fan_speed_percent},\t" \
61
- "Load: #{percent_meter amdgpu_service.busy_percent},\t" \
62
- "Power: #{amdgpu_service.power_draw} W,\t" \
63
- "Temp: #{amdgpu_service.temperature}°C "
64
- sleep seconds.to_i
65
- end
66
- end
67
-
68
- desc 'watch_csv [SECONDS]', 'Watch stats in CSV format ' \
69
- 'refreshed every n seconds defaulting to 1 second'
70
- def watch_csv(seconds=1)
71
- return puts "Seconds must be from 1 to 600" unless (1..600).cover?(seconds.to_i)
72
-
73
- puts 'Timestamp, Core Clock (Mhz),Memory Clock (Mhz),Fan speed (rpm), '\
74
- 'Load (%),Power (Watts),Temp (°C)'
75
-
76
- trap "SIGINT" do
77
- exit 0
78
- end
79
-
80
- loop do
81
- puts [Time.now.strftime("%F %T"),
82
- amdgpu_service.core_clock,
83
- amdgpu_service.memory_clock,
84
- amdgpu_service.fan_speed_rpm,
85
- amdgpu_service.busy_percent,
86
- amdgpu_service.power_draw,
87
- amdgpu_service.temperature].join(',')
88
- sleep seconds.to_i
89
- end
90
- end
91
-
92
- private
93
-
94
- def amdgpu_service
95
- @amdgpu_service ||= AmdgpuService.new
96
- end
97
-
98
- def clock_status
99
- "⏰\tClocks: #{amdgpu_service.core_clock} Core, " \
100
- "#{amdgpu_service.memory_clock} Memory"
101
- end
102
-
103
- def current_time
104
- Time.now.strftime("%F %T")
105
- end
106
-
107
- def fan_status
108
- "🌀\tFan: #{amdgpu_service.fan_mode} mode running at " \
109
- "#{amdgpu_service.fan_speed_percent}% ~ #{amdgpu_service.fan_speed_rpm} rpm"
110
- end
111
-
112
- def percent_meter(percent, length = 10)
113
- progress_bar_count = (length * percent.to_f / 100).round
114
- "[#{'|' * progress_bar_count}#{' ' * (length - progress_bar_count)}]#{percent}%"
115
- end
116
-
117
- def radeon_logo
118
- File.read(File.join(__dir__, '../lib/radeon_r_black_red_100x100.ascii'))
119
- end
120
- end
@@ -1,156 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ## AmdgpuService
4
- #
5
- # A service class for reading and interacting with AMD radeon graphics cards
6
- # through the amdgpu Linux kernel driver.
7
- class AmdgpuService
8
- BASE_FOLDER = '/sys/class/drm'
9
- FAN_MODES = { '1' => 'manual', '2' => 'auto' }.freeze
10
-
11
- attr_reader :card_num
12
-
13
- class Error < StandardError; end
14
-
15
- def initialize(card_num: 0)
16
- @card_num = card_num
17
- end
18
-
19
- def busy_percent
20
- File.read("#{base_card_folder}/gpu_busy_percent").strip
21
- end
22
-
23
- def connectors_status
24
- connectors_files.each_with_object({}) do |f, connectors|
25
- connectors[f.slice(/(?<=card0-)(\w|-)+/)] = File.read(f).strip
26
- end
27
- end
28
-
29
- def core_clock
30
- clock_from_pp_file "/sys/class/drm/card#{card_num}/device/pp_dpm_sclk"
31
- end
32
-
33
- def fan_mode
34
- FAN_MODES[File.read(fan_mode_file).strip] || 'unknown'
35
- end
36
-
37
- def set_fan_mode!(mode)
38
- `echo "#{FAN_MODES.key(mode.to_s)}" | sudo tee #{fan_mode_file}`
39
- end
40
-
41
- def fan_speed_percent
42
- (fan_speed_raw.to_f / fan_speed_raw_max.to_i * 100).round
43
- end
44
-
45
- def fan_speed_raw_max
46
- @fan_speed_raw_max ||= File.read(Dir.glob("#{base_card_folder}/**/pwm1_max").first).strip
47
- end
48
-
49
- def fan_speed_rpm
50
- File.read(fan_input_file).strip
51
- end
52
-
53
- def memory_clock
54
- clock_from_pp_file "/sys/class/drm/card#{card_num}/device/pp_dpm_mclk"
55
- end
56
-
57
- def name
58
- lspci_subsystem.split(': ')[1].strip
59
- end
60
-
61
- def power_dpm_state
62
- File.read("#{base_card_folder}/power_dpm_state").strip
63
- end
64
-
65
- def power_draw
66
- power_raw_to_watts File.read(power_avg_file)
67
- end
68
-
69
- def power_max
70
- @power_max ||= power_raw_to_watts File.read(power_max_file)
71
- end
72
-
73
- def set_fan_manual_speed!(percent: nil, raw: nil)
74
- if valid_fan_percent_speed?(percent)
75
- new_raw = (percent.to_f / 100 * fan_speed_raw_max.to_i).round
76
- elsif valid_fan_raw_speed?(raw)
77
- new_raw = raw
78
- end
79
-
80
- raise(self.class::Error, 'Invalid fan speed provided') if new_raw.to_s.empty?
81
-
82
- set_fan_mode!(:manual) unless fan_mode == 'manual'
83
-
84
- `echo "#{new_raw}" | sudo tee #{fan_power_file}`
85
- end
86
-
87
- def temperature
88
- (File.read(temperature_file).to_f / 1000).round(1)
89
- end
90
-
91
- def vbios_version
92
- @vbios_version ||= File.read("#{base_card_folder}/vbios_version").strip
93
- end
94
-
95
- private
96
-
97
- def base_card_folder
98
- @base_card_folder ||= "#{BASE_FOLDER}/card#{card_num}/device"
99
- end
100
-
101
- def clock_from_pp_file(file)
102
- File.read(file).slice /\w+(?= \*)/
103
- end
104
-
105
- def connectors_files
106
- @connectors_files ||= Dir["/sys/class/drm/card#{card_num}*/status"].sort
107
- end
108
-
109
- def fan_input_file
110
- @fan_input_file ||= Dir.glob("#{base_card_folder}/**/fan1_input").first
111
- end
112
-
113
- def fan_mode_file
114
- @fan_mode_file ||= Dir.glob("#{base_card_folder}/**/pwm1_enable").first
115
- end
116
-
117
- def fan_power_file
118
- @fan_power_file ||= Dir.glob("#{base_card_folder}/**/pwm1").first
119
- end
120
-
121
- def fan_speed_raw
122
- File.read(fan_power_file).strip
123
- end
124
-
125
- def gpu_pci_id
126
- @gpu_pci_id ||= `lspci -v | grep VGA`.split(' ').first
127
- end
128
-
129
- def lspci_subsystem
130
- @lspci_subsystem ||= `lspci -v -s #{gpu_pci_id} | grep "Subsystem:"`
131
- end
132
-
133
- def power_avg_file
134
- @power_avg_file ||= Dir.glob("#{base_card_folder}/**/power1_average").first
135
- end
136
-
137
- def power_max_file
138
- @power_max_file ||= Dir.glob("#{base_card_folder}/**/power1_cap").first
139
- end
140
-
141
- def power_raw_to_watts(raw_string)
142
- (raw_string.strip.to_f / 1_000_000).round(2)
143
- end
144
-
145
- def temperature_file
146
- @temperature_file ||= Dir.glob("#{base_card_folder}/**/temp1_input").first
147
- end
148
-
149
- def valid_fan_raw_speed?(raw)
150
- (1..fan_speed_raw_max.to_i).include?(raw.to_i)
151
- end
152
-
153
- def valid_fan_percent_speed?(percent)
154
- (1..100.to_i).include?(percent.to_i)
155
- end
156
- end
@@ -1,40 +0,0 @@
1
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
2
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
3
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
4
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
5
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
6
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
7
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
8
- ''''''''''''''.                                        .''''''''''''''''''''''''
9
- ''''''''''''''.                                          .''''''''''''''''''''''
10
- ''''''''''''''.                                    ..'''''''''''''''''''
11
- ''''''''''''''.                                          .''''''''''''''''''
12
- ''''''''''''''.                             ..                ..''''''''''''''''
13
- ''''''''''''''.         .'''''''''''''''''''''''..           .''''''''''''''''
14
- ''''''''''''''.           ''''''''''''''''''''''''''.          .''''''''''''''''
15
- ''''''''''''''.           ''''''''''''''''''''''''''.         .''''''''''''''''
16
- ''''''''''''''.           ''''''''''''''''''''''''''.          .''''''''''''''''
17
- ''''''''''''''.           ''''''''''''''''''''''''''.        .''''''''''''''''
18
- ''''''''''''''.           ''''''''''''''''''''''''''.          .''''''''''''''''
19
- ''''''''''''''.           ''''''''''''''''''''''''''.          .''''''''''''''''
20
- ''''''''''''''.           '''''''''''''''''''''''..          .''''''''''''''''
21
- ''''''''''''''.                                               .''''''''''''''''
22
- ''''''''''''''.                                            .''''''''''''''''''
23
- ''''''''''''''.                                            ..'''''''''''''''''''
24
- ''''''''''''''.                                          .''''''''''''''''''''''
25
- ''''''''''''''.                                         .'''''''''''''''''''''''
26
- ''''''''''''''.           ''''''''''''''''..           .'''''''''''''''''''''''
27
- ''''''''''''''.           ''''''''''''''''''.           .''''''''''''''''''''''
28
- ''''''''''''''.           '''''''''''''''''''.            .''''''''''''''''''''
29
- ''''''''''''''.           ''''''''''''''''''''..            ..''''''''''''''''''
30
- ''''''''''''''.           ''''''''''''''''''''''.            .'''''''''''''''''
31
- ''''''''''''''.           '''''''''''''''''''''''..          ..'''''''''''''''
32
- ''''''''''''''.           '''''''''''''''''''''''''.             .''''''''''''''
33
- ''''''''''''''.           ''''''''''''''''''''''''''.             .'''''''''''''
34
- ''''''''''''''............''''''''''''''''''''''''''''..............''''''''''''
35
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
36
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
37
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
38
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
39
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
40
- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''