amdgpu_fan 0.1.0 → 0.2.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.
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