Commit | Line | Data |
---|---|---|
438d0d5f SK |
1 | #! /bin/bash |
2 | ||
4d314e0f SK |
3 | set -e |
4 | ||
61e5a351 SK |
5 | produce_volume() { |
6 | pactl list sinks \ | |
7 | | awk ' | |
8 | /^\tMute:/ { | |
9 | printf("%s,", $0); | |
10 | } | |
11 | /^\tVolume:/ { | |
12 | for (i=2; i<=NF; i++) printf(" %s", $i); | |
13 | }' \ | |
14 | | awk -v RS=',' ' | |
15 | /^[ \t]*Mute:/ {mute = $2} | |
16 | /^[ \t]*front-left:/ {left = $4} | |
17 | /^[ \t]*front-right:/ {right = $4} | |
18 | END { | |
19 | if (mute == "yes") { | |
20 | printf("x") | |
21 | } else { | |
22 | printf("%s %s", left, right) | |
23 | } | |
24 | } | |
25 | ' | |
26 | } | |
27 | ||
365549a9 SK |
28 | produce_mpd_state() { |
29 | echo 'status' \ | |
30 | | nc 127.0.0.1 6600 \ | |
31 | | awk ' | |
32 | { | |
33 | status[$1] = $2 | |
34 | } | |
35 | ||
36 | /^time: +[0-9]+:[0-9]+$/ { | |
37 | split($2, time, ":") | |
38 | seconds_current = time[1] | |
39 | seconds_total = time[2] | |
40 | ||
41 | hours = int(seconds_current / 60 / 60); | |
42 | secs_beyond_hours = seconds_current - (hours * 60 * 60); | |
43 | mins = int(secs_beyond_hours / 60); | |
44 | secs = secs_beyond_hours - (mins * 60); | |
45 | if (hours > 0) { | |
46 | current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs) | |
47 | } else { | |
48 | current_time = sprintf("%.2d:%.2d", mins, secs) | |
49 | } | |
50 | ||
51 | if (seconds_total > 0) { | |
52 | time_percentage = (seconds_current / seconds_total) * 100 | |
53 | current_percentage = sprintf("%d%%", time_percentage) | |
54 | } else { | |
55 | current_percentage = "~" | |
56 | } | |
57 | } | |
58 | ||
59 | END { | |
60 | state = status["state:"] | |
61 | ||
62 | if (state == "play") { | |
63 | symbol = "▶" | |
64 | } else if (state == "pause") { | |
65 | symbol = "❚❚" | |
66 | } else if (state == "stop") { | |
67 | symbol = "⬛" | |
68 | } else { | |
69 | symbol = "--" | |
70 | } | |
71 | ||
72 | printf(\ | |
73 | "%s %s %s", | |
74 | status["state:"], current_time, current_percentage\ | |
75 | ) | |
76 | } | |
77 | ' | |
78 | } | |
79 | ||
80 | produce_mpd_song() { | |
81 | echo 'currentsong' \ | |
82 | | nc 127.0.0.1 6600 \ | |
83 | | awk ' | |
84 | /^OK/ { | |
85 | next | |
86 | } | |
87 | ||
88 | { | |
89 | key = $1 | |
90 | sub("^" key " +", "") | |
91 | val = $0 | |
92 | data[key] = val | |
93 | } | |
94 | ||
95 | END { | |
96 | name = data["Name:"] | |
97 | title = data["Title:"] | |
98 | file = data["file:"] | |
99 | ||
100 | if (name) { | |
101 | out = name | |
102 | } else if (title) { | |
103 | out = title | |
104 | } else if (file) { | |
105 | last = split(file, parts, "/") | |
106 | out = parts[last] | |
107 | } else { | |
108 | out = "" | |
109 | } | |
110 | print out | |
111 | } | |
112 | ' | |
113 | } | |
438d0d5f | 114 | |
6339a4f8 | 115 | produce_weather() { |
4d314e0f SK |
116 | weather_station_id="$1" |
117 | metar -d "$weather_station_id" 2>&1 \ | |
438d0d5f SK |
118 | | awk ' |
119 | /METAR pattern not found in NOAA data/ { | |
120 | failures++ | |
121 | } | |
122 | ||
123 | /^Temperature/ { | |
124 | celsius = $3; | |
125 | fahrenheit = (celsius * (9 / 5)) + 32; | |
126 | temperature = fahrenheit | |
127 | } | |
128 | ||
129 | END { | |
130 | if (failures > 0) { | |
131 | temperature = "--" | |
132 | } | |
133 | print temperature "°F" | |
134 | }' | |
135 | } | |
136 | ||
6339a4f8 | 137 | produce_datetime() { |
438d0d5f SK |
138 | date +'%a %b %d %H:%M:%S' |
139 | } | |
140 | ||
6339a4f8 | 141 | consume() { |
438d0d5f SK |
142 | pipe="$1" |
143 | tail -f "$pipe" \ | |
144 | | stdbuf -o L awk \ | |
365549a9 SK |
145 | -v opt_debug=0 \ |
146 | -v opt_mpd_song_max_chars=10 \ | |
438d0d5f | 147 | ' |
61e5a351 SK |
148 | /^in:VOLUME/\ |
149 | { | |
150 | split_msg_parts() | |
151 | db["volume"] = msg_body | |
152 | } | |
153 | ||
365549a9 SK |
154 | /^in:MPD_STATE/\ |
155 | { | |
156 | split_msg_parts() | |
157 | db["mpd_state"] = $1 | |
158 | db["mpd_curr_song_time"] = $2 | |
159 | db["mpd_curr_song_percent"] = $3 | |
160 | } | |
161 | ||
162 | /^in:MPD_SONG/\ | |
438d0d5f | 163 | { |
365549a9 SK |
164 | split_msg_parts() |
165 | db["mpd_curr_song_name"] = msg_body | |
438d0d5f SK |
166 | } |
167 | ||
365549a9 | 168 | /^in:WEATHER/\ |
438d0d5f | 169 | { |
365549a9 SK |
170 | split_msg_parts() |
171 | db["weather_temperature"] = msg_body | |
438d0d5f SK |
172 | } |
173 | ||
365549a9 | 174 | /^in:DATE_TIME/\ |
438d0d5f | 175 | { |
365549a9 SK |
176 | split_msg_parts() |
177 | db["datetime"] = msg_body | |
178 | } | |
179 | ||
180 | /^out:BAR/\ | |
181 | { | |
182 | split_msg_parts() | |
438d0d5f SK |
183 | print make_bar() |
184 | } | |
185 | ||
365549a9 SK |
186 | |
187 | function split_msg_parts() { | |
188 | msg_head = $1 | |
189 | sub("^" msg_head " +", "") | |
190 | msg_body = $0 | |
191 | debug(msg_head, msg_body) | |
438d0d5f SK |
192 | } |
193 | ||
365549a9 | 194 | function make_bar( position, bar, sep, i, j) { |
61e5a351 | 195 | position[++i] = sprintf("(%s)", db["volume"]) |
365549a9 | 196 | position[++i] = make_status_mpd() |
438d0d5f SK |
197 | position[++i] = db["weather_temperature"] |
198 | position[++i] = db["datetime"] | |
199 | bar = "" | |
200 | sep = "" | |
201 | for (j = 1; j <= i; j++) { | |
202 | bar = bar sep position[j] | |
203 | sep = " " | |
204 | } | |
205 | return bar | |
206 | } | |
365549a9 SK |
207 | |
208 | function make_status_mpd( state, status) { | |
209 | state = db["mpd_state"] | |
210 | ||
211 | if (state == "play") { | |
212 | status = make_status_mpd_state_known("▶") | |
213 | } else if (state == "pause") { | |
214 | status = make_status_mpd_state_known("❚❚") | |
215 | } else if (state == "stop") { | |
216 | status = make_status_mpd_state_known("⬛") | |
217 | } else { | |
218 | status = make_status_mpd_state_unknown("--") | |
219 | } | |
220 | ||
221 | return sprintf("[%s]", status) | |
222 | } | |
223 | ||
224 | function make_status_mpd_state_known(symbol) { | |
225 | return sprintf(\ | |
226 | "%s %s %s %s", | |
227 | symbol, | |
228 | db["mpd_curr_song_time"], | |
229 | db["mpd_curr_song_percent"], | |
230 | substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\ | |
231 | ) | |
232 | } | |
233 | ||
234 | function make_status_mpd_state_unknown(symbol) { | |
235 | return sprintf("%s", symbol) | |
236 | } | |
237 | ||
238 | function debug(location, msg) { | |
239 | if (opt_debug) { | |
240 | print_error(location, msg) | |
241 | } | |
242 | } | |
243 | ||
244 | function print_error(location, msg) { | |
245 | print(location " ==> " msg) > "/dev/stderr" | |
246 | } | |
438d0d5f SK |
247 | ' |
248 | } | |
249 | ||
6339a4f8 | 250 | produce_bar_req() { |
438d0d5f SK |
251 | echo '' |
252 | } | |
253 | ||
254 | spawn() { | |
255 | cmd="$1" | |
256 | pipe="$2" | |
365549a9 | 257 | msg_head="$3" |
438d0d5f SK |
258 | interval="$4" |
259 | while true; do | |
365549a9 | 260 | echo "${msg_head} $($cmd)" > "$pipe" |
438d0d5f SK |
261 | sleep "$interval" |
262 | done & | |
263 | } | |
264 | ||
265 | main() { | |
4d314e0f SK |
266 | # Defaults |
267 | dir_data="$HOME/.khatus" | |
268 | weather_station_id='KJFK' | |
269 | ||
270 | # User-overrides | |
271 | OPTS=$( | |
272 | getopt \ | |
273 | -o '' \ | |
274 | -l data-dir:,weather-station: \ | |
275 | -- "$@" | |
276 | ) | |
277 | eval set -- "$OPTS" | |
278 | while true | |
279 | do | |
280 | case "$1" in | |
281 | --data-dir) | |
282 | dir_data="$2" | |
283 | shift 2 | |
284 | ;; | |
285 | --weather-station) | |
286 | weather_station_id="$2" | |
287 | shift 2 | |
288 | ;; | |
289 | --) | |
290 | shift | |
291 | break | |
292 | ;; | |
293 | esac | |
294 | done | |
295 | ||
0c0ebbe1 SK |
296 | pipe="$dir_data/khatus_data_pipe" |
297 | ||
4d314e0f SK |
298 | ( echo "Khatus starting with the following parameters:" |
299 | ( echo " dir_data|= $dir_data" | |
0c0ebbe1 | 300 | echo " pipe|= $pipe" |
4d314e0f SK |
301 | echo " weather_station_id|= $weather_station_id" |
302 | ) | column -ts\| | |
303 | echo '' | |
304 | ) >&2 | |
305 | ||
4d314e0f | 306 | mkdir -p "$dir_data" |
438d0d5f SK |
307 | rm -f "$pipe" |
308 | mkfifo "$pipe" | |
309 | ||
0c0ebbe1 SK |
310 | cmd_produce_weather="produce_weather $weather_station_id" |
311 | ||
4d314e0f | 312 | # TODO: Redirect each worker's stderr to a dedicated log file |
0c0ebbe1 SK |
313 | spawn produce_datetime "$pipe" 'in:DATE_TIME' 1 |
314 | spawn "$cmd_produce_weather" "$pipe" 'in:WEATHER' $(( 30 * 60 )) | |
315 | spawn produce_mpd_state "$pipe" 'in:MPD_STATE' 1 | |
316 | spawn produce_mpd_song "$pipe" 'in:MPD_SONG' 1 | |
317 | spawn produce_volume "$pipe" 'in:VOLUME' 1 | |
318 | spawn produce_bar_req "$pipe" 'out:BAR' 1 | |
319 | consume "$pipe" | |
438d0d5f SK |
320 | } |
321 | ||
322 | main $@ |