-
Notifications
You must be signed in to change notification settings - Fork 0
/
shell-fm_lcd_console.rb
executable file
·241 lines (207 loc) · 6.61 KB
/
shell-fm_lcd_console.rb
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/usr/bin/ruby
# Simple script that displays the artist and title from shell-fm
# on a DSP-420 LCD screen.
# Scrolls any strings that are longer than their allowed lengths.
# If a string is scrolled, it is padded with 2 spaces to the beginning and end.
# (easier to read)
require 'rubygems'
require 'socket'
require 'yaml'
require File.join(File.dirname(__FILE__), 'lib', 'rubyX2040')
require File.join(File.dirname(__FILE__), 'lib', 'widget')
# Load config.
config = YAML.load_file(File.join(File.dirname(__FILE__), "config.yml"))
Host = config["host"]
Port = config["port"]
Alarms = config["alarms"]
Update_delay = 3.0 # Delay between shell.fm refreshes.
Scroll_delay = 0.5 # speed of artist and title scrolling
DisplayUpdateInterval = 0.05 # Poll for refreshes at this interval
BacklightTimeout = 20.0 # Turn off the backlight after a delay
# Gets info from shell-fm
def shellfm_info
# Gets the 'artist', 'title', and 'remaining seconds'
cmd = "info %a||%l||%t||%R"
t = TCPSocket.new(Host, Port)
t.print cmd + "\n"
info = t.gets(nil).split("||")
t.close
return info.any? ? info : false
rescue
# On error, return false
return false
end
# Sends a cmd to the shellfm network interface.
def shellfmcmd(cmd)
t = TCPSocket.new(Host, Port)
t.print cmd + "\n"
info = t.gets(nil)
t.close
return true
rescue
false
end
def display_widget(widget)
$p.message widget.render, widget.pos
end
def splash_screen
$p.message "shell.fm LCD display", [2,1]
$p.message "(c) Nathan Broadbent", [3,1]
sleep 1
$p.clear
end
def write_static_icons
$p.write_char($p.icons["guitar"][:loc], [1,1])
$p.write_char($p.icons["cd"][:loc], [2,1])
$p.write_char($p.icons["notes"][:loc], [3,1])
end
# ----------- at_exit code ---------------------
at_exit {
# When we quit, display a final "bye" message.
$p.clear
$p.backlight false
$p.message "Bye!".center(20), [2,1]
}
# -------------- Script Start -------------------
# initialize DisplayUpdateIntervalPertelian display.
$p = Pertelian.new
# Load in some icons
Dir.glob(File.join(File.dirname(__FILE__), 'lcd_icons', '*.chr')).each_with_index do |filename, i|
$p.load_char_from_file(filename, i+1)
end
# Display initial splash screen
splash_screen
# ------------------ Paused / Playing Screen --------------
# Set up widgets.
@artist_widget = Widget.new("", [1,3], 18)
@album_widget = Widget.new("", [2,3], 18)
@title_widget = Widget.new("", [3,3], 18)
@remain_widget = Widget.new("", [4,3], 6, :time)
# Variables to detect whether or not the stream is paused.
@status = :playing
@status_cache = nil # cache for checking whether the status has changed from previous iteration
# An icon widget to show the track status
@status_widget = Widget.new("<play>", [4,1], 1, :icons)
@last_remain = 0
@key_info = Widget.new(" <next>:n <pause><play>:p <love>:l <stop>:s ", [4,10], 10, :icons)
# Static icons for track info
write_static_icons
# Initialize a variable for timing out the backlight.
@backlight_time_left = BacklightTimeout
# ------------------- Stopped Screen -----------------------
@stopped_widget = Widget.new("<stop> Stopped.", [2,1], 20, :icons, :center)
# ------------------- Initialize threads -------------------
# Thread to periodically update our artist/title/remaining time hash and loop.
shellfm_refresh_thread = Thread.new {
while true
has_changed = false
if data = shellfm_info
[@artist_widget, @title_widget, @album_widget].zip(data[0,3]).each do |widget, value|
# Reset widget scroll positions if their values have changed.
if widget.value != value
widget.value, widget.scroll_pos = value, 1
has_changed = true
end
end
# Set the remaining track time.
@remain_widget.value = data[3]
# Detect whether the stream is paused or not.
currently_playing = (data[3].to_i > 0 && @last_remain != data[3])
@last_remain = data[3]
if @status == :playing
unless currently_playing
@status = :paused
# Update the status icon to 'paused'
@status_widget.value = "<pause>"
has_changed = true
end
else
if currently_playing
@status = :playing
@status_widget.value = "<play>"
has_changed = true
end
end
else # else, if shellfm_info method returned false or nil, the track is stopped.
if @status != :stopped
@status = :stopped
has_changed = true
end
end
# Turn on the backlight if a value has changed.
if has_changed
@backlight_time_left = BacklightTimeout
end
sleep Update_delay
end
}
# Thread to count down the remaining time between refreshes (if stream is playing)
countdown_remain_thread = Thread.new {
while true
@remain_widget.value = (@remain_widget.value.to_i - 1).to_s if @status == :playing
sleep 1
end
}
# Thread to scroll widgets.
scroll_thread = Thread.new {
while true
[@artist_widget, @title_widget, @album_widget, @key_info].each do |widget|
widget.increment_scroll
end
sleep Scroll_delay
end
}
# Thread for alarms (trigger actions at configured times.)
alarm_thread = Thread.new {
while true
time = Time.now
Alarms.each do |alarm|
if alarm["days_of_week"].include?(t.wday)
alarm = DateTime.parse(alarm["time"])
if alarm.hour == time.hour && alarm.min == time.min
# Alarm matches, trigger action.
case alarm["action"]
when "play"
shellfmcmd("play lastfm://#{alarm["station"]}")
when "pause"
shellfmcmd("pause")
end
end
end
end
# Wait until the next minute, then loop.
while Time.now.min == time.min
sleep 5
end
end
}
# Loop to refresh widgets when needed. Also control backlight.
while true
case @status
when :playing, :paused
if @status_cache == :stopped # Redraw static icons
write_static_icons
@status_cache = @status
end
[@artist_widget, @title_widget, @album_widget, @remain_widget, @key_info, @status_widget].each do |widget|
display_widget(widget) if widget.needs_refresh
end
when :stopped
if @status_cache != :stopped
$p.clear
display_widget(@stopped_widget)
@status_cache = @status
end
end
# Also timeout the backlight, if needed
if @backlight_time_left > 0
# If a thread has just set the backlight timeout, turn it on.
$p.backlight true if @backlight_time_left == BacklightTimeout
@backlight_time_left -= DisplayUpdateInterval
if @backlight_time_left <= 0
@backlight_time_left = 0.0
$p.backlight false
end
end
sleep DisplayUpdateInterval
end