amdgpu_fan 0.3.0 → 0.7.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: d8bd1ed519a47f577ba017c94f03ac02101767434796821120aef3f9e12d9ba9
4
- data.tar.gz: 6d55c0d1c45d076d1409318332fade5d9038c8f4e64b86d8af2cb2d5eeb016df
3
+ metadata.gz: a499ce52639612b25843b04c3497c7c36f23f3a960d0946b7dc2e249e9a18874
4
+ data.tar.gz: fa87ca342988d97a91773b1272829a3b825b88dbd65420d6abd26fed8d97ac92
5
5
  SHA512:
6
- metadata.gz: dba9441882e19a730fe7863ac49ac196d6ac6b6ed4f55a1f086d2467c122cb58b7e57fb3dc27d0bfbddcaba9344a89992b59fce9c1f52edb44feb54e9a718b97
7
- data.tar.gz: 75b810c93258bbb39b2973f97e029cd602d70a61314da9e08af1a13fcaea5ec2ea7067e424aa0a3358d5a9b1863937cefa3a095897214f6ca8f0453757fd5250
6
+ metadata.gz: 06b3bd73191b39da57ac9c14e7ada8fa2cf1a069e8401f45af10eab6ede0bf79a5ebd2f80ba5c464e60f960e24d11783cf20d698c640a38e36c80846b305772e
7
+ data.tar.gz: 794e28fd6ae71a898b1e897a0fd612add4585e901ece8bc3928e62de9d8245107767848352fe7203d4aa5b6ec60c4e78b28523cc0c893d4d0bcb2f67fdd52044
data/README.md CHANGED
@@ -1,10 +1,17 @@
1
- # amdgpu-fan-rb
1
+ # amdgpu_fan
2
2
 
3
- [![Build Status](https://travis-ci.org/HarlemSquirrel/amdgpu-fan-rb.svg?branch=master)](https://travis-ci.org/HarlemSquirrel/amdgpu-fan-rb)
3
+ [![Gem Version](https://badge.fury.io/rb/amdgpu_fan.svg)](https://badge.fury.io/rb/amdgpu_fan)
4
+ [![Build Status](https://travis-ci.org/HarlemSquirrel/amdgpu-fan-rb.svg?branch=master)](https://travis-ci.org/HarlemSquirrel/amdgpu-fan-rb) [![Maintainability](https://api.codeclimate.com/v1/badges/27233cee17ef6a2c14fd/maintainability)](https://codeclimate.com/github/HarlemSquirrel/amdgpu-fan-rb/maintainability)
4
5
 
5
- A Ruby CLI to read and set the fan speed for AMD Radeon graphics cards running on the AMDGPU Linux driver.
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
- See https://wiki.archlinux.org/index.php/Fan_speed_control#AMDGPU_sysfs_fan_control
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 auto # Set mode to automatic (requires sudo)
34
- amdgpu_fan help [COMMAND] # Describe available commands or one specific command
35
- amdgpu_fan set PERCENTAGE # Set fan speed to PERCENTAGE (requires sudo)
36
- amdgpu_fan status # View device info, current fan speed, and temperature
37
- amdgpu_fan watch [SECONDS] # Watch current fan speed, and temperature refreshed every n seconds
38
-
39
- amdgpu_fan status
40
- 📺 GPU: AMD Radeon (TM) R9 Fury Series
41
- 📄 vBIOS: 113-C8800100-102
42
- 🌀 Fan: auto mode running at 48% ~ 1828 rpm
43
- 🌡 Temp: 28.0°C
44
- ⚡ Power: 19.26 / 300.0 Watts
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-10 20:36:01 - Clock: 512Mhz Core, 500Mhz Memory, Fan: 1838 rpm [||| ]27%, Load: [||| ]28%, Power: 15.12 W, Temp: 28.0°C
50
- 2019-05-10 20:36:05 - Clock: 300Mhz Core, 500Mhz Memory, Fan: 1837 rpm [||||| ]49%, Load: [ ]0%, Power: 16.18 W, Temp: 29.0°C
51
- 2019-05-10 20:36:09 - Clock: 512Mhz Core, 500Mhz Memory, Fan: 1837 rpm [||||| ]49%, Load: [ ]0%, Power: 15.11 W, Temp: 28.0°C
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
- - [`lspci`](https://linux.die.net/man/8/lspci) (included with most Linux distributions)
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
+ ```
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../lib/amdgpu_fan_cli'
4
+ require_relative '../lib/amdgpu_fan'
5
5
 
6
- AmdgpuFanCli.start ARGV
6
+ AmdgpuFan::Cli.start ARGV
@@ -1,3 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
4
 
3
- require_relative '../lib/amdgpu_service'
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'
@@ -0,0 +1,10 @@
1
+ ---
2
+ clock: ⏰
3
+ display: 📺
4
+ fan: 🌀
5
+ gpu: 👾
6
+ load: "⚖ "
7
+ memory: 💾
8
+ power: ⚡
9
+ temp: "🌡 "
10
+ vbios: 📄
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../config/environment'
4
+
5
+ # The main module
6
+ module AmdgpuFan
7
+ end
@@ -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