Commit | Line | Data |
---|---|---|
438d0d5f SK |
1 | #! /bin/bash |
2 | ||
4d314e0f SK |
3 | set -e |
4 | ||
0d116e88 SK |
5 | produce_net_wifi_status() { |
6 | nmcli \ | |
7 | -f ACTIVE,SSID,SIGNAL \ | |
8 | -t \ | |
9 | d wifi \ | |
10 | | awk \ | |
11 | -F ':' \ | |
12 | ' | |
13 | BEGIN {wifi_status = "--"} | |
14 | $1 == "yes" {wifi_status = $2 ":" $3 "%"} | |
15 | END {print wifi_status} | |
16 | ' | |
17 | } | |
18 | ||
ad0b3a5b SK |
19 | produce_bluetooth_power() { |
20 | echo -e 'show \n quit' \ | |
21 | | bluetoothctl \ | |
22 | | awk ' | |
23 | /^Controller / { | |
24 | controller = $2; | |
25 | controllers[++ctrl_count] = controller; | |
26 | } | |
27 | /^\t[A-Z][A-Za-z]+:/ { | |
28 | key = $1; | |
29 | sub(":$", "", key); | |
30 | val = $2; | |
31 | for (i=3; i<=NF; i++) { | |
32 | val = val " " $i}; | |
33 | data[controller, key] = val; | |
34 | } | |
35 | END { | |
36 | # Using the 1st seen controller. Should we select specific instead? | |
37 | power_status = data[controllers[1], "Powered"]; | |
38 | if (ctrl_count > 0) { | |
39 | if (power_status == "no") { | |
40 | power_status = "off" | |
41 | } else if (power_status == "yes") { | |
42 | power_status = "on" | |
43 | } else { | |
44 | printf("Unexpected bluetooth power status: %s\n", power_status)\ | |
45 | > "/dev/stderr"; | |
46 | power_status = "ERROR" | |
47 | } | |
48 | } else { | |
49 | power_status = "off" # TODO: Perhaps use differentiated marker? | |
50 | } | |
51 | printf("%s", power_status); | |
52 | }' | |
53 | } | |
54 | ||
bbb42518 SK |
55 | produce_screen_brightness() { |
56 | screen_brightness_device_path="$1" | |
57 | echo "\ | |
58 | $(cat $screen_brightness_device_path/max_brightness) \ | |
59 | $(cat $screen_brightness_device_path/brightness)\ | |
60 | " | |
61 | } | |
62 | ||
61e5a351 SK |
63 | produce_volume() { |
64 | pactl list sinks \ | |
65 | | awk ' | |
66 | /^\tMute:/ { | |
67 | printf("%s,", $0); | |
68 | } | |
69 | /^\tVolume:/ { | |
70 | for (i=2; i<=NF; i++) printf(" %s", $i); | |
71 | }' \ | |
72 | | awk -v RS=',' ' | |
73 | /^[ \t]*Mute:/ {mute = $2} | |
74 | /^[ \t]*front-left:/ {left = $4} | |
75 | /^[ \t]*front-right:/ {right = $4} | |
76 | END { | |
77 | if (mute == "yes") { | |
78 | printf("x") | |
79 | } else { | |
80 | printf("%s %s", left, right) | |
81 | } | |
82 | } | |
83 | ' | |
84 | } | |
85 | ||
365549a9 SK |
86 | produce_mpd_state() { |
87 | echo 'status' \ | |
88 | | nc 127.0.0.1 6600 \ | |
89 | | awk ' | |
90 | { | |
91 | status[$1] = $2 | |
92 | } | |
93 | ||
94 | /^time: +[0-9]+:[0-9]+$/ { | |
95 | split($2, time, ":") | |
96 | seconds_current = time[1] | |
97 | seconds_total = time[2] | |
98 | ||
99 | hours = int(seconds_current / 60 / 60); | |
100 | secs_beyond_hours = seconds_current - (hours * 60 * 60); | |
101 | mins = int(secs_beyond_hours / 60); | |
102 | secs = secs_beyond_hours - (mins * 60); | |
103 | if (hours > 0) { | |
104 | current_time = sprintf("%d:%.2d:%.2d", hours, mins, secs) | |
105 | } else { | |
106 | current_time = sprintf("%.2d:%.2d", mins, secs) | |
107 | } | |
108 | ||
109 | if (seconds_total > 0) { | |
110 | time_percentage = (seconds_current / seconds_total) * 100 | |
111 | current_percentage = sprintf("%d%%", time_percentage) | |
112 | } else { | |
113 | current_percentage = "~" | |
114 | } | |
115 | } | |
116 | ||
117 | END { | |
118 | state = status["state:"] | |
119 | ||
120 | if (state == "play") { | |
121 | symbol = "▶" | |
122 | } else if (state == "pause") { | |
123 | symbol = "❚❚" | |
124 | } else if (state == "stop") { | |
125 | symbol = "⬛" | |
126 | } else { | |
127 | symbol = "--" | |
128 | } | |
129 | ||
130 | printf(\ | |
131 | "%s %s %s", | |
132 | status["state:"], current_time, current_percentage\ | |
133 | ) | |
134 | } | |
135 | ' | |
136 | } | |
137 | ||
138 | produce_mpd_song() { | |
139 | echo 'currentsong' \ | |
140 | | nc 127.0.0.1 6600 \ | |
141 | | awk ' | |
142 | /^OK/ { | |
143 | next | |
144 | } | |
145 | ||
146 | { | |
147 | key = $1 | |
148 | sub("^" key " +", "") | |
149 | val = $0 | |
150 | data[key] = val | |
151 | } | |
152 | ||
153 | END { | |
154 | name = data["Name:"] | |
155 | title = data["Title:"] | |
156 | file = data["file:"] | |
157 | ||
158 | if (name) { | |
159 | out = name | |
160 | } else if (title) { | |
161 | out = title | |
162 | } else if (file) { | |
163 | last = split(file, parts, "/") | |
164 | out = parts[last] | |
165 | } else { | |
166 | out = "" | |
167 | } | |
168 | print out | |
169 | } | |
170 | ' | |
171 | } | |
438d0d5f | 172 | |
6339a4f8 | 173 | produce_weather() { |
4d314e0f SK |
174 | weather_station_id="$1" |
175 | metar -d "$weather_station_id" 2>&1 \ | |
438d0d5f SK |
176 | | awk ' |
177 | /METAR pattern not found in NOAA data/ { | |
178 | failures++ | |
179 | } | |
180 | ||
181 | /^Temperature/ { | |
182 | celsius = $3; | |
183 | fahrenheit = (celsius * (9 / 5)) + 32; | |
184 | temperature = fahrenheit | |
185 | } | |
186 | ||
187 | END { | |
188 | if (failures > 0) { | |
189 | temperature = "--" | |
190 | } | |
191 | print temperature "°F" | |
192 | }' | |
193 | } | |
194 | ||
6339a4f8 | 195 | produce_datetime() { |
438d0d5f SK |
196 | date +'%a %b %d %H:%M:%S' |
197 | } | |
198 | ||
6339a4f8 | 199 | consume() { |
438d0d5f SK |
200 | pipe="$1" |
201 | tail -f "$pipe" \ | |
202 | | stdbuf -o L awk \ | |
365549a9 SK |
203 | -v opt_debug=0 \ |
204 | -v opt_mpd_song_max_chars=10 \ | |
438d0d5f | 205 | ' |
0d116e88 SK |
206 | /^in:NET_WIFI_STATUS/\ |
207 | { | |
208 | split_msg_parts() | |
209 | db["net_wifi_status"] = msg_body | |
210 | } | |
211 | ||
ad0b3a5b SK |
212 | /^in:BLUETOOTH_POWER/\ |
213 | { | |
214 | split_msg_parts() | |
215 | db["bluetooth_power"] = msg_body | |
216 | } | |
217 | ||
bbb42518 SK |
218 | /^in:SCREEN_BRIGHTNESS/\ |
219 | { | |
220 | split_msg_parts() | |
221 | set_screen_brightness() | |
222 | } | |
223 | ||
61e5a351 SK |
224 | /^in:VOLUME/\ |
225 | { | |
226 | split_msg_parts() | |
227 | db["volume"] = msg_body | |
228 | } | |
229 | ||
365549a9 SK |
230 | /^in:MPD_STATE/\ |
231 | { | |
232 | split_msg_parts() | |
233 | db["mpd_state"] = $1 | |
234 | db["mpd_curr_song_time"] = $2 | |
235 | db["mpd_curr_song_percent"] = $3 | |
236 | } | |
237 | ||
238 | /^in:MPD_SONG/\ | |
438d0d5f | 239 | { |
365549a9 SK |
240 | split_msg_parts() |
241 | db["mpd_curr_song_name"] = msg_body | |
438d0d5f SK |
242 | } |
243 | ||
365549a9 | 244 | /^in:WEATHER/\ |
438d0d5f | 245 | { |
365549a9 SK |
246 | split_msg_parts() |
247 | db["weather_temperature"] = msg_body | |
438d0d5f SK |
248 | } |
249 | ||
365549a9 | 250 | /^in:DATE_TIME/\ |
438d0d5f | 251 | { |
365549a9 SK |
252 | split_msg_parts() |
253 | db["datetime"] = msg_body | |
254 | } | |
255 | ||
256 | /^out:BAR/\ | |
257 | { | |
258 | split_msg_parts() | |
438d0d5f SK |
259 | print make_bar() |
260 | } | |
261 | ||
365549a9 | 262 | |
bbb42518 SK |
263 | function set_screen_brightness( max, cur) { |
264 | max = $1 | |
265 | cur = $2 | |
266 | db["screen_brightness"] = (cur / max) * 100 | |
267 | } | |
268 | ||
365549a9 SK |
269 | function split_msg_parts() { |
270 | msg_head = $1 | |
271 | sub("^" msg_head " +", "") | |
272 | msg_body = $0 | |
273 | debug(msg_head, msg_body) | |
438d0d5f SK |
274 | } |
275 | ||
365549a9 | 276 | function make_bar( position, bar, sep, i, j) { |
0d116e88 | 277 | position[++i] = make_status_net() |
ad0b3a5b | 278 | position[++i] = sprintf("B=%s", db["bluetooth_power"]) |
bbb42518 | 279 | position[++i] = sprintf("*%d%%", db["screen_brightness"]) |
61e5a351 | 280 | position[++i] = sprintf("(%s)", db["volume"]) |
365549a9 | 281 | position[++i] = make_status_mpd() |
438d0d5f SK |
282 | position[++i] = db["weather_temperature"] |
283 | position[++i] = db["datetime"] | |
284 | bar = "" | |
285 | sep = "" | |
286 | for (j = 1; j <= i; j++) { | |
287 | bar = bar sep position[j] | |
288 | sep = " " | |
289 | } | |
290 | return bar | |
291 | } | |
365549a9 | 292 | |
0d116e88 SK |
293 | function make_status_net() { |
294 | return sprintf("N[w:%s]", db["net_wifi_status"]) | |
295 | } | |
296 | ||
365549a9 SK |
297 | function make_status_mpd( state, status) { |
298 | state = db["mpd_state"] | |
299 | ||
300 | if (state == "play") { | |
301 | status = make_status_mpd_state_known("▶") | |
302 | } else if (state == "pause") { | |
303 | status = make_status_mpd_state_known("❚❚") | |
304 | } else if (state == "stop") { | |
305 | status = make_status_mpd_state_known("⬛") | |
306 | } else { | |
307 | status = make_status_mpd_state_unknown("--") | |
308 | } | |
309 | ||
310 | return sprintf("[%s]", status) | |
311 | } | |
312 | ||
313 | function make_status_mpd_state_known(symbol) { | |
314 | return sprintf(\ | |
315 | "%s %s %s %s", | |
316 | symbol, | |
317 | db["mpd_curr_song_time"], | |
318 | db["mpd_curr_song_percent"], | |
319 | substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\ | |
320 | ) | |
321 | } | |
322 | ||
323 | function make_status_mpd_state_unknown(symbol) { | |
324 | return sprintf("%s", symbol) | |
325 | } | |
326 | ||
327 | function debug(location, msg) { | |
328 | if (opt_debug) { | |
329 | print_error(location, msg) | |
330 | } | |
331 | } | |
332 | ||
333 | function print_error(location, msg) { | |
334 | print(location " ==> " msg) > "/dev/stderr" | |
335 | } | |
438d0d5f SK |
336 | ' |
337 | } | |
338 | ||
6339a4f8 | 339 | produce_bar_req() { |
438d0d5f SK |
340 | echo '' |
341 | } | |
342 | ||
343 | spawn() { | |
344 | cmd="$1" | |
345 | pipe="$2" | |
365549a9 | 346 | msg_head="$3" |
438d0d5f SK |
347 | interval="$4" |
348 | while true; do | |
365549a9 | 349 | echo "${msg_head} $($cmd)" > "$pipe" |
438d0d5f SK |
350 | sleep "$interval" |
351 | done & | |
352 | } | |
353 | ||
354 | main() { | |
4d314e0f SK |
355 | # Defaults |
356 | dir_data="$HOME/.khatus" | |
357 | weather_station_id='KJFK' | |
bbb42518 | 358 | screen_brightness_device_name='acpi_video0' |
4d314e0f SK |
359 | |
360 | # User-overrides | |
361 | OPTS=$( | |
362 | getopt \ | |
363 | -o '' \ | |
bbb42518 | 364 | -l data-dir:,weather-station:screen-device: \ |
4d314e0f SK |
365 | -- "$@" |
366 | ) | |
367 | eval set -- "$OPTS" | |
368 | while true | |
369 | do | |
370 | case "$1" in | |
371 | --data-dir) | |
372 | dir_data="$2" | |
373 | shift 2 | |
374 | ;; | |
375 | --weather-station) | |
376 | weather_station_id="$2" | |
377 | shift 2 | |
378 | ;; | |
bbb42518 SK |
379 | --screen-device) |
380 | screen_brightness_device_name="$2" | |
381 | shift 2 | |
382 | ;; | |
4d314e0f SK |
383 | --) |
384 | shift | |
385 | break | |
386 | ;; | |
387 | esac | |
388 | done | |
389 | ||
0c0ebbe1 | 390 | pipe="$dir_data/khatus_data_pipe" |
bbb42518 SK |
391 | screen_brightness_device_path='/sys/class/backlight' |
392 | screen_brightness_device_path+="/$screen_brightness_device_name" | |
0c0ebbe1 | 393 | |
4d314e0f SK |
394 | ( echo "Khatus starting with the following parameters:" |
395 | ( echo " dir_data|= $dir_data" | |
0c0ebbe1 | 396 | echo " pipe|= $pipe" |
bbb42518 SK |
397 | echo " screen_brightness_device_name|=$screen_brightness_device_name" |
398 | echo " screen_brightness_device_path|=$screen_brightness_device_path" | |
4d314e0f SK |
399 | echo " weather_station_id|= $weather_station_id" |
400 | ) | column -ts\| | |
401 | echo '' | |
402 | ) >&2 | |
403 | ||
4d314e0f | 404 | mkdir -p "$dir_data" |
438d0d5f SK |
405 | rm -f "$pipe" |
406 | mkfifo "$pipe" | |
407 | ||
bbb42518 SK |
408 | cmd_produce_screen_brightness='produce_screen_brightness' |
409 | cmd_produce_screen_brightness+=" $screen_brightness_device_path" | |
0c0ebbe1 SK |
410 | cmd_produce_weather="produce_weather $weather_station_id" |
411 | ||
4d314e0f | 412 | # TODO: Redirect each worker's stderr to a dedicated log file |
0c0ebbe1 | 413 | spawn produce_datetime "$pipe" 'in:DATE_TIME' 1 |
bbb42518 | 414 | spawn "$cmd_produce_screen_brightness" "$pipe" 'in:SCREEN_BRIGHTNESS' 1 |
0c0ebbe1 SK |
415 | spawn "$cmd_produce_weather" "$pipe" 'in:WEATHER' $(( 30 * 60 )) |
416 | spawn produce_mpd_state "$pipe" 'in:MPD_STATE' 1 | |
417 | spawn produce_mpd_song "$pipe" 'in:MPD_SONG' 1 | |
418 | spawn produce_volume "$pipe" 'in:VOLUME' 1 | |
ad0b3a5b | 419 | spawn produce_bluetooth_power "$pipe" 'in:BLUETOOTH_POWER' 5 |
0d116e88 | 420 | spawn produce_net_wifi_status "$pipe" 'in:NET_WIFI_STATUS' 5 |
0c0ebbe1 SK |
421 | spawn produce_bar_req "$pipe" 'out:BAR' 1 |
422 | consume "$pipe" | |
438d0d5f SK |
423 | } |
424 | ||
425 | main $@ |