amdgpu_fan 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5628dc7eec5a2fb4737be7e3f79cad3d51d1359457162568303a12f7f9886ee
4
- data.tar.gz: 98ed0f072b9b8174b1160a4460bb80631c3e5c7263c244cf148dbb738a26fd16
3
+ metadata.gz: 474b8500f7505a2793cf2a3cc1345d39273c78b2749cde1a40bef0f8050910e0
4
+ data.tar.gz: ceb407543897bd36b851c1c1c42660e8f146878cd354ee0411a104b9358fb133
5
5
  SHA512:
6
- metadata.gz: ceed05049dd35db984ba670ad0bee7336c6693fb16a83c77bedebd8fa327ccff0b63bc84dde4436f9a2a9654f00f5284086a66aff8d6cd6f358760160865bbe9
7
- data.tar.gz: ae319ad4e4acc088930b435f886527b0c226203ab3743e2794ae03f0f969178f51f9315cf4f28212f17a31c20daa1a52e2aa717495a1b23aed88d1d3809acb39
6
+ metadata.gz: 2808b9d2b5ce15880b9ee3937bac0f534c81b66c3824fe44439ebfc7449ba52d7ad44e930cc21f201bedd1bc6b34a3d17f3d469aed62a534fc8e6cc005064a09
7
+ data.tar.gz: 2ddc2aeedb9648308e7bf39f7421d5ae5397addd9df7ed35e6f15e903c78b8c36c946df1a48e529e2cdd2de7629696e0e7caa67d0923d65d9b2b72f56aad7bcd
data/README.md CHANGED
@@ -7,10 +7,27 @@ See https://wiki.archlinux.org/index.php/Fan_speed_control#AMDGPU_sysfs_fan_cont
7
7
  ## Usage
8
8
 
9
9
  ```
10
- bin/amdgpu_fan
10
+ bundle
11
+
12
+ ➤ bin/amdgpu_fan help
11
13
  Commands:
12
- amdgpu_fan auto # set mode to automatic (requires sudo)
13
- amdgpu_fan help [COMMAND] # Describe available commands or one specific command
14
- amdgpu_fan set PERCENTAGE # set fan speed to PERCENTAGE (requires sudo)
15
- amdgpu_fan status # report the current status
14
+ amdgpu_fan auto # Set mode to automatic (requires sudo)
15
+ amdgpu_fan help [COMMAND] # Describe available commands or one specific command
16
+ amdgpu_fan set PERCENTAGE # Set fan speed to PERCENTAGE (requires sudo)
17
+ amdgpu_fan status # View device info, current fan speed, and temperature
18
+ amdgpu_fan watch [SECONDS] # Watch current fan speed, and temperature refreshed every n seconds
19
+
20
+ ➤ bin/amdgpu_fan status
21
+ 📺 GPU: AMD Radeon (TM) R9 Fury Series
22
+ 📄 vBIOS: 113-C8800100-102
23
+ 🌀 Fan: auto mode running at 48% ~ 1828 rpm
24
+ 🌡 Temp: 28.0°C
25
+ ⚡ Power: 19.26 / 300.0 Watts
16
26
  ```
27
+
28
+ ## Dependencies
29
+
30
+ - Ruby
31
+ - bundler
32
+ - thor (installed with bundler)
33
+ - lspci (included with most Linux distributions)
@@ -0,0 +1,3 @@
1
+ require 'thor'
2
+
3
+ require_relative '../lib/amdgpu_service'
@@ -1,104 +1,77 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
3
+ require_relative '../config/environment'
4
4
 
5
5
  # The main class
6
6
  class AmdgpuFanCli < Thor
7
- FAN_POWER_FILE = Dir.glob("/sys/class/drm/card0/device/**/pwm1").first
8
- FAN_MAX_POWER_FILE = Dir.glob("/sys/class/drm/card0/device/**/pwm1_max").first
9
- FAN_INPUT_FILE = Dir.glob("/sys/class/drm/card0/device/**/fan1_input").first
10
- FAN_MODE_FILE = Dir.glob("/sys/class/drm/card0/device/**/pwm1_enable").first
11
- FAN_MODES = { auto: '2', manual: '1' }.freeze
12
- POWER_MAX_FILE = '/sys/class/drm/card0/device/hwmon/hwmon2/power1_cap'
13
- POWER_AVG_FILE = '/sys/class/drm/card0/device/hwmon/hwmon2/power1_average'
14
- TEMPERATURE_FILE = Dir.glob("/sys/class/drm/card0/device/**/temp1_input").first
15
- VBIOS_VERSION = File.read('/sys/class/drm/card0/device/vbios_version').strip
16
-
17
7
  desc 'auto', 'Set mode to automatic (requires sudo)'
18
8
  def auto
19
- set_mode(:auto)
9
+ amdgpu_service.set_fan_mode! :auto
10
+ puts fan_status
20
11
  end
21
12
 
22
13
  desc 'set PERCENTAGE', 'Set fan speed to PERCENTAGE (requires sudo)'
23
14
  def set(percentage)
24
15
  return puts "Invalid percentage" unless (0..100).cover?(percentage.to_i)
25
- set_mode(:manual) unless in_manual_mode?
26
- puts "Setting fan to #{setting_from_percent percentage}/#{max}..."
27
- set_manual_speed setting_from_percent(percentage)
16
+
17
+ amdgpu_service.set_fan_manual_speed! percent: percentage
18
+ puts fan_status
19
+ rescue AmdgpuService::Error
20
+ puts 'Invalid fan speed provided. The percentage should be between 1 and 100'
21
+ exit 1
28
22
  end
29
23
 
30
24
  desc 'status', 'View device info, current fan speed, and temperature'
31
25
  def status
32
- puts device_info,
33
- "Video BIOS version: #{VBIOS_VERSION}",
34
- "🌀\tFan: #{current_mode} mode running at #{current_percentage.round}% ~ #{rpm} rpm",
35
- "🌡\tTemp: #{current_temperature}°C",
36
- "⚡\tPower: #{current_power} / #{power_max} Watts"
37
- end
38
-
39
- private
40
-
41
- def device_info
42
- @device_info ||= `glxinfo | grep -m 1 -o "AMD Radeon .* Series"`.strip
43
- end
44
-
45
- def current
46
- File.read FAN_POWER_FILE
47
- end
48
-
49
- def current_mode
50
- case File.read(FAN_MODE_FILE).strip
51
- when '1'
52
- 'manual'
53
- when '2'
54
- 'auto'
55
- else
56
- 'unknown'
26
+ print_radeon_logo
27
+ puts "📺\tGPU: #{amdgpu_service.name}",
28
+ "📄\tvBIOS: #{amdgpu_service.vbios_version}",
29
+ fan_status,
30
+ "🌡\tTemp: #{amdgpu_service.temperature}°C",
31
+ "⚡\tPower: #{amdgpu_service.power_dpm_state} mode using " \
32
+ "#{amdgpu_service.power_draw} / #{amdgpu_service.power_max} Watts",
33
+ "⚖\tLoad: #{amdgpu_service.busy_percent}%"
34
+ end
35
+
36
+ desc 'watch [SECONDS]', 'Watch fan speed, load, power, and temperature ' \
37
+ 'refreshed every n seconds'
38
+ def watch(seconds=1)
39
+ return puts "Seconds must be from 1 to 600" unless (1..600).cover?(seconds.to_i)
40
+
41
+ puts "Watching #{amdgpu_service.name} every #{seconds} second(s)...",
42
+ ' <Press Ctrl-C to exit>'
43
+
44
+ trap "SIGINT" do
45
+ puts 'And now the watch is ended.'
46
+ exit 0
57
47
  end
58
- end
59
-
60
- def current_percentage
61
- current.to_f / max.to_i * 100
62
- end
63
-
64
- def current_power
65
- (File.read(POWER_AVG_FILE).strip.to_f / 1000000).round(2)
66
- end
67
-
68
- def current_temperature
69
- (File.read(TEMPERATURE_FILE).to_f / 1000).round(1)
70
- end
71
-
72
- def max
73
- @max ||= File.read(FAN_MAX_POWER_FILE).to_i
74
- end
75
-
76
- def in_auto_mode?
77
- File.read(FAN_MODE_FILE).strip == '2'
78
- end
79
48
 
80
- def in_manual_mode?
81
- File.read(FAN_MODE_FILE).strip == '1'
49
+ loop do
50
+ puts "#{Time.now.strftime("%F %T")} " \
51
+ "Fan: #{amdgpu_service.fan_speed_rpm} rpm (#{amdgpu_service.fan_speed_percent}%), " \
52
+ "Load: #{amdgpu_service.busy_percent}%, " \
53
+ "Power: #{amdgpu_service.power_draw} W, " \
54
+ "Temp: #{amdgpu_service.temperature}°C "
55
+ sleep seconds.to_i
56
+ end
82
57
  end
83
58
 
84
- def power_max
85
- @power_max ||= (File.read(POWER_MAX_FILE).strip.to_f / 1000000).round(2)
86
- end
59
+ private
87
60
 
88
- def rpm
89
- File.read(FAN_INPUT_FILE).strip
61
+ def amdgpu_service
62
+ @amdgpu_service ||= AmdgpuService.new
90
63
  end
91
64
 
92
- def set_mode(mode)
93
- puts "Setting mode to #{mode}"
94
- `echo "#{FAN_MODES[mode]}" | sudo tee #{FAN_MODE_FILE}`
65
+ def current_time
66
+ Time.now.strftime("%F %T")
95
67
  end
96
68
 
97
- def set_manual_speed(speed)
98
- `echo "#{speed}" | sudo tee #{FAN_POWER_FILE}`
69
+ def fan_status
70
+ "🌀\tFan: #{amdgpu_service.fan_mode} mode running at " \
71
+ "#{amdgpu_service.fan_speed_percent}% ~ #{amdgpu_service.fan_speed_rpm} rpm"
99
72
  end
100
73
 
101
- def setting_from_percent(percent)
102
- (percent.to_f / 100 * max.to_i).round
74
+ def print_radeon_logo
75
+ puts File.read('lib/radeon_r_black_red_100x100.ascii')
103
76
  end
104
77
  end
@@ -0,0 +1,134 @@
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 fan_mode
24
+ FAN_MODES[File.read(fan_mode_file).strip] || 'unknown'
25
+ end
26
+
27
+ def set_fan_mode!(mode)
28
+ `echo "#{FAN_MODES.key(mode.to_s)}" | sudo tee #{fan_mode_file}`
29
+ end
30
+
31
+ def fan_speed_percent
32
+ (fan_speed_raw.to_f / fan_speed_raw_max.to_i * 100).round
33
+ end
34
+
35
+ def fan_speed_raw_max
36
+ @fan_speed_raw_max ||= File.read(Dir.glob("#{base_card_folder}/**/pwm1_max").first).strip
37
+ end
38
+
39
+ def fan_speed_rpm
40
+ File.read(fan_input_file).strip
41
+ end
42
+
43
+ def name
44
+ lspci_subsystem.split(': ')[1].strip
45
+ end
46
+
47
+ def power_dpm_state
48
+ File.read("#{base_card_folder}/power_dpm_state").strip
49
+ end
50
+
51
+ def power_draw
52
+ power_raw_to_watts File.read(power_avg_file)
53
+ end
54
+
55
+ def power_max
56
+ @power_max ||= power_raw_to_watts File.read(power_max_file)
57
+ end
58
+
59
+ def set_fan_manual_speed!(percent: nil, raw: nil)
60
+ if valid_fan_percent_speed?(percent)
61
+ new_raw = (percent.to_f / 100 * fan_speed_raw_max.to_i).round
62
+ elsif valid_fan_raw_speed?(raw)
63
+ new_raw = raw
64
+ end
65
+
66
+ raise(self.class::Error, 'Invalid fan speed provided') if new_raw.to_s.empty?
67
+
68
+ set_fan_mode!(:manual) unless fan_mode == 'manual'
69
+
70
+ `echo "#{new_raw}" | sudo tee #{fan_power_file}`
71
+ end
72
+
73
+ def temperature
74
+ (File.read(temperature_file).to_f / 1000).round(1)
75
+ end
76
+
77
+ def vbios_version
78
+ @vbios_version ||= File.read("#{base_card_folder}/vbios_version").strip
79
+ end
80
+
81
+ private
82
+
83
+ def base_card_folder
84
+ @base_card_folder ||= "#{BASE_FOLDER}/card#{card_num}/device"
85
+ end
86
+
87
+ def fan_input_file
88
+ @fan_input_file ||= Dir.glob("#{base_card_folder}/**/fan1_input").first
89
+ end
90
+
91
+ def fan_mode_file
92
+ @fan_mode_file ||= Dir.glob("#{base_card_folder}/**/pwm1_enable").first
93
+ end
94
+
95
+ def fan_power_file
96
+ @fan_power_file ||= Dir.glob("#{base_card_folder}/**/pwm1").first
97
+ end
98
+
99
+ def fan_speed_raw
100
+ File.read(fan_power_file).strip
101
+ end
102
+
103
+ def gpu_pci_id
104
+ @gpu_pci_id ||= `lspci -v | grep VGA`.split(' ').first
105
+ end
106
+
107
+ def lspci_subsystem
108
+ @lspci_subsystem ||= `lspci -v -s #{gpu_pci_id} | grep "Subsystem:"`
109
+ end
110
+
111
+ def power_avg_file
112
+ @power_avg_file ||= Dir.glob("#{base_card_folder}/**/power1_average").first
113
+ end
114
+
115
+ def power_max_file
116
+ @power_avg_file ||= Dir.glob("#{base_card_folder}/**/power1_cap").first
117
+ end
118
+
119
+ def power_raw_to_watts(raw_string)
120
+ (raw_string.strip.to_f / 1_000_000).round(2)
121
+ end
122
+
123
+ def temperature_file
124
+ @temperature_file ||= Dir.glob("#{base_card_folder}/**/temp1_input").first
125
+ end
126
+
127
+ def valid_fan_raw_speed?(raw)
128
+ (1..fan_speed_raw_max.to_i).include?(raw.to_i)
129
+ end
130
+
131
+ def valid_fan_percent_speed?(percent)
132
+ (1..100.to_i).include?(percent.to_i)
133
+ end
134
+ end
@@ -0,0 +1,40 @@
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
+ ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amdgpu_fan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin McCormack
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-07 00:00:00.000000000 Z
11
+ date: 2019-01-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A CLI for amdgpu fans
14
14
  email: harlemsquirrel@gmail.com
@@ -19,7 +19,10 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - README.md
21
21
  - bin/amdgpu_fan
22
+ - config/environment.rb
22
23
  - lib/amdgpu_fan_cli.rb
24
+ - lib/amdgpu_service.rb
25
+ - lib/radeon_r_black_red_100x100.ascii
23
26
  homepage: https://github.com/HarlemSquirrel/amdgpu-fan-rb
24
27
  licenses:
25
28
  - MIT