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