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.
- checksums.yaml +4 -4
- data/README.md +65 -21
- data/bin/amdgpu_fan +2 -2
- data/config/environment.rb +13 -1
- data/config/icons.yml +10 -0
- data/lib/amdgpu_fan.rb +7 -0
- data/lib/amdgpu_fan/cli.rb +203 -0
- data/lib/amdgpu_fan/connector.rb +71 -0
- data/lib/amdgpu_fan/mixin/cli_output_format.rb +25 -0
- data/lib/amdgpu_fan/mixin/fan.rb +42 -0
- data/lib/amdgpu_fan/mixin/sys_write.rb +14 -0
- data/lib/amdgpu_fan/pci.rb +62 -0
- data/lib/amdgpu_fan/service.rb +189 -0
- data/lib/amdgpu_fan/stat_set.rb +24 -0
- data/lib/amdgpu_fan/version.rb +6 -0
- data/lib/amdgpu_fan/watcher.rb +55 -0
- metadata +62 -11
- data/lib/amdgpu_fan_cli.rb +0 -120
- data/lib/amdgpu_service.rb +0 -156
- data/lib/radeon_r_black_red_100x100.ascii +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a499ce52639612b25843b04c3497c7c36f23f3a960d0946b7dc2e249e9a18874
|
|
4
|
+
data.tar.gz: fa87ca342988d97a91773b1272829a3b825b88dbd65420d6abd26fed8d97ac92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06b3bd73191b39da57ac9c14e7ada8fa2cf1a069e8401f45af10eab6ede0bf79a5ebd2f80ba5c464e60f960e24d11783cf20d698c640a38e36c80846b305772e
|
|
7
|
+
data.tar.gz: 794e28fd6ae71a898b1e897a0fd612add4585e901ece8bc3928e62de9d8245107767848352fe7203d4aa5b6ec60c4e78b28523cc0c893d4d0bcb2f67fdd52044
|
data/README.md
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# amdgpu_fan
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/amdgpu_fan)
|
|
4
|
+
[](https://travis-ci.org/HarlemSquirrel/amdgpu-fan-rb) [](https://codeclimate.com/github/HarlemSquirrel/amdgpu-fan-rb/maintainability)
|
|
4
5
|
|
|
5
|
-
A Ruby CLI to read and set
|
|
6
|
+
A Ruby CLI to read and set fan speed, power profiles, and more for AMD Radeon graphics cards running on the AMDGPU Linux driver.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
**amdgpu_fan** aims to provide a more user friendly interface on top of [sysfs](https://en.wikipedia.org/wiki/Sysfs) for displaying statistics and interacting with AMD Radeon graphics hardware running on the [AMDgpu](https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html) driver.
|
|
9
|
+
|
|
10
|
+
#### Further reading
|
|
11
|
+
|
|
12
|
+
- https://wiki.archlinux.org/index.php/AMDGPU#Overclocking
|
|
13
|
+
- https://wiki.archlinux.org/index.php/Fan_speed_control#AMDGPU_sysfs_fan_control
|
|
14
|
+
- https://phoronix.com/scan.php?page=news_item&px=AMDGPU-Quick-WattMan-Cap-Test
|
|
8
15
|
|
|
9
16
|
## Installation
|
|
10
17
|
|
|
@@ -28,27 +35,56 @@ gem install amdgpu_fan
|
|
|
28
35
|
## Usage
|
|
29
36
|
|
|
30
37
|
```
|
|
31
|
-
➤ amdgpu_fan help
|
|
38
|
+
➤ bin/amdgpu_fan help
|
|
32
39
|
Commands:
|
|
33
|
-
amdgpu_fan
|
|
34
|
-
amdgpu_fan
|
|
35
|
-
amdgpu_fan
|
|
36
|
-
amdgpu_fan
|
|
37
|
-
amdgpu_fan
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
amdgpu_fan connectors # View the status of the display connectors.
|
|
41
|
+
amdgpu_fan fan # View fan details.
|
|
42
|
+
amdgpu_fan fan_set PERCENTAGE/AUTO # Set fan speed to percentage or automatic mode. (requires sudo)
|
|
43
|
+
amdgpu_fan help [COMMAND] # Describe available commands or one specific command
|
|
44
|
+
amdgpu_fan profile # View power profile details.
|
|
45
|
+
amdgpu_fan profile_auto # Set the power profile to automatic mode.
|
|
46
|
+
amdgpu_fan profile_force PROFILE_NUM # Manually set a power profile. (requires sudo)
|
|
47
|
+
amdgpu_fan status [--logo] # View device info, current fan speed, and temperature.
|
|
48
|
+
amdgpu_fan watch [SECONDS] # Watch fan speed, load, power, and temperature refreshed every n seconds.
|
|
49
|
+
amdgpu_fan watch_csv [SECONDS] # Watch stats in CSV format refreshed every n seconds defaulting to 1 second.
|
|
50
|
+
|
|
51
|
+
➤ bin/amdgpu_fan status
|
|
52
|
+
📺 GPU: Advanced Micro Devices, Inc. [AMD/ATI] Radeon R9 FURY X / NANO
|
|
53
|
+
📄 vBIOS: 113-C8800100-102
|
|
54
|
+
⏰ Clocks: 724Mhz Core, 500Mhz Memory
|
|
55
|
+
💾 Memory: 4096 MiB
|
|
56
|
+
🌀 Fan: auto mode running at 1809 rpm (48%)
|
|
57
|
+
🌞 Temp: 21.0°C
|
|
58
|
+
⚡ Power: 3D_FULL_SCREEN profile in performance mode using 16.2 / 300.0 Watts (5%)
|
|
59
|
+
⚖ Load: [ ]0%
|
|
45
60
|
|
|
46
61
|
➤ bin/amdgpu_fan watch 3
|
|
47
62
|
Watching Advanced Micro Devices, Inc. [AMD/ATI] Radeon R9 FURY X / NANO every 3 second(s)...
|
|
48
63
|
<Press Ctrl-C to exit>
|
|
49
|
-
2019-05-
|
|
50
|
-
2019-05-
|
|
51
|
-
2019-05-
|
|
64
|
+
2019-05-28 20:57:41 | Core: 724Mhz | Memory: 500Mhz | Fan: 948 rpm [* ]14% | Load: [** ]24% | Power: 16.07 W [* ]6% | Temp: 34.0°C
|
|
65
|
+
2019-05-28 20:57:45 | Core: 512Mhz | Memory: 500Mhz | Fan: 948 rpm [* ]14% | Load: [ ]0% | Power: 16.13 W [* ]7% | Temp: 34.0°C
|
|
66
|
+
2019-05-28 20:57:49 | Core: 892Mhz | Memory: 500Mhz | Fan: 948 rpm [* ]14% | Load: [ ]0% | Power: 25.22 W [* ]5% | Temp: 33.0°C
|
|
67
|
+
2019-05-28 20:57:53 | Core: 300Mhz | Memory: 500Mhz | Fan: 948 rpm [* ]14% | Load: [ ]0% | Power: 19.1 W [* ]6% | Temp: 33.0°C
|
|
68
|
+
2019-05-28 20:57:57 | Core: 1050Mhz | Memory: 500Mhz | Fan: 948 rpm [* ]14% | Load: [********* ]94% | Power: 103.04 W [*** ]31% | Temp: 36.0°C
|
|
69
|
+
2019-05-28 20:58:01 | Core: 1050Mhz | Memory: 500Mhz | Fan: 954 rpm [** ]15% | Load: [********* ]91% | Power: 158.07 W [***** ]53% | Temp: 38.0°C
|
|
70
|
+
2019-05-28 20:58:05 | Core: 1050Mhz | Memory: 500Mhz | Fan: 977 rpm [** ]16% | Load: [**********]100% | Power: 218.01 W [******* ]73% | Temp: 40.0°C
|
|
71
|
+
2019-05-28 20:58:09 | Core: 1050Mhz | Memory: 500Mhz | Fan: 1005 rpm [** ]16% | Load: [**********]100% | Power: 216.24 W [******* ]71% | Temp: 40.0°C
|
|
72
|
+
2019-05-28 20:58:13 | Core: 1050Mhz | Memory: 500Mhz | Fan: 1033 rpm [** ]17% | Load: [**********]97% | Power: 109.25 W [**** ]39% | Temp: 38.0°C
|
|
73
|
+
2019-05-28 20:58:17 | Core: 724Mhz | Memory: 500Mhz | Fan: 1058 rpm [** ]17% | Load: [ ]0% | Power: 17.17 W [* ]6% | Temp: 35.0°C
|
|
74
|
+
^C
|
|
75
|
+
And now the watch is ended.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
➤ bin/amdgpu_fan watch_avg
|
|
80
|
+
Watching Sapphire Technology Limited Vega 10 XL/XT [Radeon RX Vega 56/64] min, max and averges since
|
|
81
|
+
2020-06-02 23:05:20 -0400...
|
|
82
|
+
<Press Ctrl-C to exit>
|
|
83
|
+
⏰ Core clock min: 852 MHz avg: 887.0 MHz max: 1200 MHz now: 852 MHz
|
|
84
|
+
💾 Memory clk min: 167 MHz avg: 227.1 MHz max: 945 MHz now: 167 MHz
|
|
85
|
+
🌀 Fan speed min: 1231 RPM avg: 1231.0 RPM max: 1231 RPM now: 1231 RPM
|
|
86
|
+
⚡ Power usage min: 6.0 W avg: 21.8 W max: 141.0 W now: 6.0 W
|
|
87
|
+
🌡 Temperature min: 30 °C avg: 31.3 °C max: 35 °C now: 32 °C
|
|
52
88
|
^C
|
|
53
89
|
And now the watch is ended.
|
|
54
90
|
```
|
|
@@ -57,4 +93,12 @@ And now the watch is ended.
|
|
|
57
93
|
|
|
58
94
|
- [Ruby](https://www.ruby-lang.org) with [Bundler](https://bundler.io)
|
|
59
95
|
- [Thor](http://whatisthor.com/) (installed with `bundle install`)
|
|
60
|
-
-
|
|
96
|
+
- Internet connection - Device info is retrieved from https://pci-ids.ucw.cz/
|
|
97
|
+
|
|
98
|
+
## Building a binary
|
|
99
|
+
|
|
100
|
+
[Ruby Packer](https://github.com/pmq20/ruby-packer) provides a convenient way to compile this into a single executable. For the best results, compile Ruby Packer from source from the lastest master branch.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
rubyc amdgpu_fan --output amdgpu_fan
|
|
104
|
+
```
|
data/bin/amdgpu_fan
CHANGED
data/config/environment.rb
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
|
|
3
|
-
require_relative '../lib/
|
|
5
|
+
require_relative '../lib/amdgpu_fan'
|
|
6
|
+
require_relative '../lib/amdgpu_fan/version'
|
|
7
|
+
|
|
8
|
+
require_relative '../lib/amdgpu_fan/mixin/cli_output_format'
|
|
9
|
+
require_relative '../lib/amdgpu_fan/mixin/sys_write'
|
|
10
|
+
|
|
11
|
+
require_relative '../lib/amdgpu_fan/service'
|
|
12
|
+
require_relative '../lib/amdgpu_fan/cli'
|
|
13
|
+
require_relative '../lib/amdgpu_fan/pci'
|
|
14
|
+
require_relative '../lib/amdgpu_fan/stat_set'
|
|
15
|
+
require_relative '../lib/amdgpu_fan/watcher'
|
data/config/icons.yml
ADDED
data/lib/amdgpu_fan.rb
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module AmdgpuFan
|
|
6
|
+
# The command-line interface class
|
|
7
|
+
class Cli < Thor
|
|
8
|
+
include CliOutputFormat
|
|
9
|
+
|
|
10
|
+
ICONS = YAML.safe_load(File.read(File.join(__dir__, '../../config/icons.yml')))
|
|
11
|
+
.transform_keys(&:to_sym).freeze
|
|
12
|
+
WATCH_FIELD_SEPARATOR = ' | '
|
|
13
|
+
|
|
14
|
+
desc 'connectors', 'View the status of the display connectors.'
|
|
15
|
+
def connectors
|
|
16
|
+
amdgpu_service.connectors.each do |connector|
|
|
17
|
+
puts "#{connector.type} #{connector.index}:\t" +
|
|
18
|
+
(connector.connected? ? connector.display_name : connector.status)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc 'profile', 'View power profile details.'
|
|
23
|
+
def profile
|
|
24
|
+
puts amdgpu_service.profile_summary
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc 'profile_auto', 'Set the power profile to automatic mode.'
|
|
28
|
+
def profile_auto
|
|
29
|
+
amdgpu_service.profile_auto
|
|
30
|
+
puts amdgpu_service.profile_summary
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc 'profile_force PROFILE_NUM', 'Manually set a power profile. (requires sudo)'
|
|
34
|
+
def profile_force(state)
|
|
35
|
+
amdgpu_service.profile_force = state
|
|
36
|
+
puts amdgpu_service.profile_summary
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc 'fan', 'View fan details.'
|
|
40
|
+
def fan
|
|
41
|
+
puts fan_status
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc 'fan_set PERCENTAGE/AUTO', 'Set fan speed to percentage or automatic mode. (requires sudo)'
|
|
45
|
+
def fan_set(value)
|
|
46
|
+
if value.strip.casecmp('auto').zero?
|
|
47
|
+
amdgpu_service.fan_mode = :auto
|
|
48
|
+
else
|
|
49
|
+
return puts 'Invalid percentage' unless (0..100).cover?(value.to_i)
|
|
50
|
+
|
|
51
|
+
amdgpu_service.fan_speed = value
|
|
52
|
+
end
|
|
53
|
+
puts fan_status
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc 'status [--logo]', 'View device info, current fan speed, and temperature.'
|
|
57
|
+
def status(option = nil)
|
|
58
|
+
puts radeon_logo if option == '--logo'
|
|
59
|
+
puts ICONS[:gpu] + ' GPU:'.ljust(9) + amdgpu_service.name,
|
|
60
|
+
ICONS[:vbios] + ' vBIOS:'.ljust(9) + amdgpu_service.vbios_version,
|
|
61
|
+
ICONS[:display] + ' Displays:' + amdgpu_service.display_names.join(', '),
|
|
62
|
+
ICONS[:clock] + ' Clocks:'.ljust(9) + clock_status,
|
|
63
|
+
ICONS[:memory] + ' Memory:'.ljust(9) + mem_total_mibibyes,
|
|
64
|
+
ICONS[:fan] + ' Fan:'.ljust(9) + fan_status,
|
|
65
|
+
ICONS[:temp] + ' Temp:'.ljust(9) + "#{amdgpu_service.temperature}°C",
|
|
66
|
+
ICONS[:power] + ' Power:'.ljust(9) + "#{amdgpu_service.profile_mode} profile in " \
|
|
67
|
+
"#{amdgpu_service.power_dpm_state} mode using " \
|
|
68
|
+
"#{amdgpu_service.power_draw} / #{amdgpu_service.power_max} Watts "\
|
|
69
|
+
"(#{amdgpu_service.power_draw_percent}%)",
|
|
70
|
+
ICONS[:load] + ' Load:'.ljust(9) + percent_meter(amdgpu_service.busy_percent, 20)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc 'version', 'Print the application version.'
|
|
74
|
+
def version
|
|
75
|
+
puts AmdgpuFan::VERSION
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc 'watch [SECONDS]', 'Watch fan speed, load, power, and temperature ' \
|
|
79
|
+
'refreshed every n seconds.'
|
|
80
|
+
def watch(seconds = 1)
|
|
81
|
+
return puts 'Seconds must be from 1 to 600' unless (1..600).cover?(seconds.to_i)
|
|
82
|
+
|
|
83
|
+
puts "Watching #{amdgpu_service.name} every #{seconds} second(s)...",
|
|
84
|
+
' <Press Ctrl-C to exit>'
|
|
85
|
+
|
|
86
|
+
trap 'SIGINT' do
|
|
87
|
+
puts "\nAnd now the watch is ended."
|
|
88
|
+
exit 0
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
loop do
|
|
92
|
+
time = Time.now
|
|
93
|
+
puts [time.strftime('%F %T'), summary_clock, summary_fan, summary_load, summary_power,
|
|
94
|
+
summary_temp].join(WATCH_FIELD_SEPARATOR)
|
|
95
|
+
|
|
96
|
+
# It can take a second or two to run the above so we remove them from the wait
|
|
97
|
+
# here to get a more consistant watch interval.
|
|
98
|
+
sec_left_to_wait = time.to_i + seconds.to_i - Time.now.to_i
|
|
99
|
+
sleep sec_left_to_wait if sec_left_to_wait.positive?
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
desc 'watch_avg',
|
|
104
|
+
<<~DOC
|
|
105
|
+
Watch min, max, average, and current stats.
|
|
106
|
+
DOC
|
|
107
|
+
def watch_avg
|
|
108
|
+
puts "Watching #{amdgpu_service.name} min, max and averges since #{Time.now}...",
|
|
109
|
+
' <Press Ctrl-C to exit>',
|
|
110
|
+
"\n\n\n\n\n"
|
|
111
|
+
|
|
112
|
+
trap 'SIGINT' do
|
|
113
|
+
puts "\nAnd now the watch is ended."
|
|
114
|
+
exit 0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
watcher = Watcher.new amdgpu_service
|
|
118
|
+
|
|
119
|
+
loop do
|
|
120
|
+
watcher.measure
|
|
121
|
+
5.times { print "\033[K\033[A" } # move up a line and clear to end of line
|
|
122
|
+
|
|
123
|
+
puts ICONS[:clock] + ' Core clock ' + watcher.core_clock.to_s,
|
|
124
|
+
ICONS[:memory] + ' Memory clk ' + watcher.mem_clock.to_s,
|
|
125
|
+
ICONS[:fan] + ' Fan speed ' + watcher.fan_speed.to_s,
|
|
126
|
+
ICONS[:power] + ' Power usage ' + watcher.power.to_s,
|
|
127
|
+
ICONS[:temp] + ' Temperature ' + watcher.temp.to_s
|
|
128
|
+
sleep 1
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
desc 'watch_csv [SECONDS]', 'Watch stats in CSV format ' \
|
|
133
|
+
'refreshed every n seconds defaulting to 1 second.'
|
|
134
|
+
def watch_csv(seconds = 1)
|
|
135
|
+
return puts 'Seconds must be from 1 to 600' unless (1..600).cover?(seconds.to_i)
|
|
136
|
+
|
|
137
|
+
puts 'Timestamp, Core Clock (Mhz),Memory Clock (Mhz),Fan speed (rpm), '\
|
|
138
|
+
'Load (%),Power (Watts),Temp (°C)'
|
|
139
|
+
|
|
140
|
+
trap 'SIGINT' do
|
|
141
|
+
exit 0
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
loop do
|
|
145
|
+
puts [Time.now.strftime('%F %T'),
|
|
146
|
+
amdgpu_service.core_clock,
|
|
147
|
+
amdgpu_service.memory_clock,
|
|
148
|
+
amdgpu_service.fan_speed_rpm,
|
|
149
|
+
amdgpu_service.busy_percent,
|
|
150
|
+
amdgpu_service.power_draw,
|
|
151
|
+
amdgpu_service.temperature].join(',')
|
|
152
|
+
sleep seconds.to_i
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
def amdgpu_service
|
|
159
|
+
@amdgpu_service ||= AmdgpuFan::Service.new
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def clock_status
|
|
163
|
+
"#{amdgpu_service.core_clock} Core, #{amdgpu_service.memory_clock} Memory"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def fan_status
|
|
167
|
+
"#{amdgpu_service.fan_mode} mode running at " \
|
|
168
|
+
"#{amdgpu_service.fan_speed_rpm} rpm (#{amdgpu_service.fan_speed_percent}%)"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def mem_total_mibibyes
|
|
172
|
+
"#{amdgpu_service.memory_total / (2**20)} MiB"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def power_max
|
|
176
|
+
format('%<num>0.2f', num: amdgpu_service.power_max)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def summary_clock
|
|
180
|
+
"Core: #{amdgpu_service.core_clock.rjust(7)}#{WATCH_FIELD_SEPARATOR}"\
|
|
181
|
+
"Memory: #{amdgpu_service.memory_clock.rjust(7)}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def summary_fan
|
|
185
|
+
fan_speed_string = "#{amdgpu_service.fan_speed_rpm} rpm".rjust(8)
|
|
186
|
+
"Fan: #{fan_speed_string} #{percent_meter(amdgpu_service.fan_speed_percent)}"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def summary_load
|
|
190
|
+
"Load: #{percent_meter amdgpu_service.busy_percent}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def summary_power
|
|
194
|
+
"Power: #{format('%<num>0.02f', num: amdgpu_service.power_draw).rjust(power_max.length)} W" \
|
|
195
|
+
" #{percent_meter amdgpu_service.power_draw_percent}"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def summary_temp
|
|
199
|
+
temp_string = "#{amdgpu_service.temperature}°C".rjust(7)
|
|
200
|
+
"Temp: #{temp_string}"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AmdgpuFan
|
|
4
|
+
## Connector
|
|
5
|
+
#
|
|
6
|
+
# A model class for a GPU connector
|
|
7
|
+
class Connector
|
|
8
|
+
EDID_DESCRIPTORS_CONF = {
|
|
9
|
+
display_name_leading_bytes: String.new('\x00\xFC\x00', encoding: 'ascii-8bit'),
|
|
10
|
+
unspecified_text_leading_bytes: String.new('\x00\xFE\x00', encoding: 'ascii-8bit'),
|
|
11
|
+
index_range: (54..125)
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
attr_reader :card_num, :dir_path, :index, :type
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
##
|
|
18
|
+
# Return an array of connector objects for the provided card number.
|
|
19
|
+
# The files are sorted to improve how they are displayed to the user.
|
|
20
|
+
def where(card_num:)
|
|
21
|
+
Dir["/sys/class/drm/card#{card_num}/card#{card_num}-*"].sort.map do |dir_path|
|
|
22
|
+
Connector.new card_num: card_num,
|
|
23
|
+
dir_path: dir_path,
|
|
24
|
+
index: dir_path[-1],
|
|
25
|
+
type: dir_path.slice(/(?<=card#{card_num}-)[A-z]+/)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(card_num:, dir_path:, index:, type:)
|
|
31
|
+
@card_num = card_num
|
|
32
|
+
@dir_path = dir_path
|
|
33
|
+
@index = index
|
|
34
|
+
@type = type
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def connected?
|
|
38
|
+
status.casecmp('connected').zero?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def display_name
|
|
42
|
+
return if edid.to_s.empty?
|
|
43
|
+
|
|
44
|
+
(display_name_text + unspecified_text).join(' ').strip
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def status
|
|
48
|
+
File.read(File.join(dir_path, 'status')).strip
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def display_descriptors_raw
|
|
54
|
+
edid.slice EDID_DESCRIPTORS_CONF[:index_range]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def display_name_text
|
|
58
|
+
display_descriptors_raw
|
|
59
|
+
.scan(/(?<=#{EDID_DESCRIPTORS_CONF[:display_name_leading_bytes]}).{1,13}/)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def edid
|
|
63
|
+
File.read("#{dir_path}/edid", encoding: 'ascii-8bit')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def unspecified_text
|
|
67
|
+
display_descriptors_raw
|
|
68
|
+
.scan(/(?<=#{EDID_DESCRIPTORS_CONF[:unspecified_text_leading_bytes]}).{1,13}/)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AmdgpuFan
|
|
4
|
+
# A mixin to help with CLI output formatting
|
|
5
|
+
module CliOutputFormat
|
|
6
|
+
METER_CHAR = '*'
|
|
7
|
+
TIME_FORMAT = '%F %T'
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def current_time
|
|
12
|
+
Time.now.strftime(TIME_FORMAT)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def percent_meter(percent, length = 10)
|
|
16
|
+
progress_bar_count = (length * percent.to_f / 100).round
|
|
17
|
+
percent_string = "#{format '%<num>0.2i', num: percent}%".ljust(3)
|
|
18
|
+
"[#{METER_CHAR * progress_bar_count}#{' ' * (length - progress_bar_count)}]#{percent_string}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def radeon_logo
|
|
22
|
+
File.read(File.join(__dir__, '../../../assets/radeon_r_black_red_100x100.ascii'))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|