Fix abnormal energy status output
[khatus.git] / bin / khatus_controller
1 #! /usr/bin/awk -f
2
3 /^in:ENERGY/\
4 {
5 split_msg_parts()
6 sub("%$", "", $2)
7 db["energy_state"] = $1
8 db["energy_percentage"] = $2
9 }
10
11 /^in:MEMORY/\
12 {
13 split_msg_parts()
14 db["memory_total"] = $1
15 db["memory_used"] = $2
16 }
17
18 /^in:FAN +status:/\
19 {
20 split_msg_parts()
21 db["fan_status"] = $2
22 }
23
24 /^in:FAN +speed:/\
25 {
26 split_msg_parts()
27 db["fan_speed"] = $2
28 }
29
30 /^in:FAN +level:/\
31 {
32 split_msg_parts()
33 db["fan_level"] = $2
34 }
35
36 /^in:TEMPERATURE/\
37 {
38 split_msg_parts()
39 db["temperature"] = $1
40 }
41
42 /^in:LOAD_AVG/\
43 {
44 split_msg_parts()
45 set_load_avg()
46 }
47
48 /^in:DISK_IO/\
49 {
50 split_msg_parts()
51 set_disk_io()
52 }
53
54 /^in:DISK_SPACE/\
55 {
56 split_msg_parts()
57 db["disk_space_used"] = msg_body
58 }
59
60 /^in:NET_ADDR_IO/\
61 {
62 split_msg_parts()
63 set_net_addr_io()
64 }
65
66 /^in:NET_WIFI_STATUS/\
67 {
68 split_msg_parts()
69 db["net_wifi_status"] = msg_body
70 }
71
72 /^in:BLUETOOTH_POWER/\
73 {
74 split_msg_parts()
75 db["bluetooth_power"] = msg_body
76 }
77
78 /^in:SCREEN_BRIGHTNESS/\
79 {
80 split_msg_parts()
81 set_screen_brightness()
82 }
83
84 /^in:VOLUME/\
85 {
86 split_msg_parts()
87 db["volume"] = msg_body
88 }
89
90 /^in:MPD_STATE/\
91 {
92 split_msg_parts()
93 db["mpd_state"] = $1
94 db["mpd_curr_song_time"] = $2
95 db["mpd_curr_song_percent"] = $3
96 }
97
98 /^in:MPD_SONG/\
99 {
100 split_msg_parts()
101 db["mpd_curr_song_name"] = msg_body
102 }
103
104 /^in:WEATHER/\
105 {
106 split_msg_parts()
107 db["weather_temperature"] = msg_body
108 }
109
110 /^in:DATE_TIME/\
111 {
112 split_msg_parts()
113 db["datetime"] = msg_body
114 output_msg_status_bar(make_status_bar())
115 }
116
117 {
118 alert_check_all()
119 }
120
121 function alert_check_all() {
122 alert_check_energy()
123 }
124
125 # TODO: Generalize alert spec lang
126 # - trigger threshold
127 # - above/bellow/equal to threshold value
128 # - priority
129 # - snooze time (if already alerted, when to re-alert?)
130 # - text: subject/body
131 function alert_check_energy( state, remaining, subj, body) {
132 state = db["energy_state"]
133 remaining = db["energy_percentage"]
134 if (state == "discharging") {
135 if (remaining < 5) {
136 subj = "Energy_CRITICALLY_Low"
137 body = sprintf("%d%% CHARGE NOW!!! GO GO GO!!!", remaining)
138 alert_trigger_hi(subj, body)
139 } else if (remaining < 10) {
140 subj = "Energy_Very_Low"
141 body = sprintf("%d%% Plug it in ASAP.", remaining)
142 alert_trigger_hi(subj, body)
143 } else if (remaining < 15) {
144 subj = "Energy_Low"
145 body = sprintf("%d%% Get the charger.", remaining)
146 alert_trigger_hi(subj, body)
147 } else if (remaining < 50) {
148 if (!state__alerts__energy__notified_bellow_half) {
149 state__alerts__energy__notified_bellow_half = 1
150 subj = "Energy_Bellow_Half"
151 body = sprintf("%d%% Where is the charger?", remaining)
152 alert_trigger_hi(subj, body)
153 }
154 }
155 } else {
156 # TODO: Reconsider the competing global-state organizing-conventions
157 state__alerts__energy__notified_bellow_half = 0
158 }
159 }
160
161 function alert_trigger_low(subject, body) {
162 alert_trigger("low", subject, body)
163 }
164
165 function alert_trigger_med(subject, body) {
166 alert_trigger("med", subject, body)
167 }
168
169 function alert_trigger_hi(subject, body) {
170 alert_trigger("hi", subject, body)
171 }
172
173 function alert_trigger(priority, subject, body, msg) {
174 # priority : "low" | "med" | "hi"
175 # subject : no spaces
176 # body : anything
177 msg = sprintf("%s %s %s", priority, subject, body)
178 output_msg_alert(msg)
179 }
180
181 function output_msg_alert(msg) {
182 # TODO: Should alerts go into a dedicated channel?
183 output_msg("ALERT", msg, "/dev/stdout")
184 }
185
186 function output_msg_status_bar(msg) {
187 output_msg("STATUS_BAR", msg, "/dev/stdout")
188 }
189
190 function output_msg(type, content, channel) {
191 print(type, content) > channel
192 }
193
194 function set_load_avg( sched) {
195 split($4, sched, "/")
196 db["load_avg_1min"] = $1
197 db["load_avg_5min"] = $2
198 db["load_avg_15min"] = $3
199 db["kern_sched_queue_runnable"] = sched[1]
200 db["kern_sched_queue_total"] = sched[2]
201 db["kern_sched_latest_pid"] = $5
202 }
203
204 function set_disk_io( curr_w, curr_r, prev_w, prev_r) {
205 curr_w = $1
206 curr_r = $2
207 prev_w = db["disk_io_curr_w"]
208 prev_r = db["disk_io_curr_r"]
209 db["disk_io_curr_w"] = curr_w
210 db["disk_io_curr_r"] = curr_r
211 db["disk_io_diff_w"] = curr_w - prev_w
212 db["disk_io_diff_r"] = curr_r - prev_r
213 }
214
215 function set_net_addr_io( \
216 interface, address, io_curr_w, io_curr_r, io_prev_w, io_prev_r\
217 ) {
218 interface = $1
219 address = $2
220 io_curr_w = $3
221 io_curr_r = $4
222 if (interface) {
223 if (address && io_curr_w && io_curr_r) {
224 # recalculate
225 io_prev_w = net_io_curr_w[interface]
226 io_prev_r = net_io_curr_r[interface]
227
228 net_addr[interface] = address
229 net_io_curr_w[interface] = io_curr_w
230 net_io_curr_r[interface] = io_curr_r
231 net_io_diff_w[interface] = io_curr_w - io_prev_w
232 net_io_diff_r[interface] = io_curr_r - io_prev_r
233 } else {
234 # clear
235 net_addr[interface] = ""
236 net_io_curr_w[interface] = 0
237 net_io_curr_r[interface] = 0
238 net_io_diff_w[interface] = 0
239 net_io_diff_r[interface] = 0
240 }
241 }
242 }
243
244 function set_screen_brightness( max, cur) {
245 max = $1
246 cur = $2
247 db["screen_brightness"] = (cur / max) * 100
248 }
249
250 function split_msg_parts() {
251 msg_head = $1
252 sub("^" msg_head " +", "")
253 msg_body = $0
254 debug(msg_head, msg_body)
255 }
256
257 function make_status_bar( position, bar, sep, i, j) {
258 position[++i] = make_status_energy()
259 position[++i] = make_status_mem()
260 position[++i] = make_status_cpu()
261 position[++i] = make_status_disk()
262 position[++i] = make_status_net()
263 position[++i] = sprintf("B=%s", db["bluetooth_power"])
264 position[++i] = sprintf("*%d%%", db["screen_brightness"])
265 position[++i] = sprintf("(%s)", db["volume"])
266 position[++i] = make_status_mpd()
267 position[++i] = db["weather_temperature"]
268 position[++i] = db["datetime"]
269 bar = ""
270 sep = ""
271 for (j = 1; j <= i; j++) {
272 bar = bar sep position[j]
273 sep = " "
274 }
275 return bar
276 }
277
278 function make_status_energy( state, direction_of_change) {
279 state = db["energy_state"]
280 if (state == "discharging") {
281 direction_of_change = "<"
282 } else if (state == "charging") {
283 direction_of_change = ">"
284 } else {
285 direction_of_change = "="
286 };
287 return sprintf("E%s%s%%", direction_of_change, db["energy_percentage"])
288 }
289
290 function make_status_mem( total, used, percent, status) {
291 total = db["memory_total"]
292 used = db["memory_used"]
293 # To avoid division by zero when data is missing
294 if (total && used) {
295 percent = round((used / total) * 100)
296 status = sprintf("%d%%", percent)
297 } else {
298 status = "__"
299 }
300 return sprintf("M=%s", status)
301 }
302
303 function make_status_cpu( load, temp, fan) {
304 load = db["load_avg_1min"]
305 temp = db["temperature"] / 1000
306 fan = db["fan_speed"]
307 return sprintf("C=[%4.2f %d°C %4drpm]", load, temp, fan)
308 }
309
310 function make_status_disk( bytes_per_sector, bytes_per_mb, w, r) {
311 bytes_per_sector = 512
312 bytes_per_mb = 1024 * 1024
313 w = (db["disk_io_diff_w"] * bytes_per_sector) / bytes_per_mb
314 r = (db["disk_io_diff_r"] * bytes_per_sector) / bytes_per_mb
315 return \
316 sprintf("D=[%s %0.3f▲ %0.3f▼]", db["disk_space_used"], w, r)
317 }
318
319 function make_status_net( \
320 out,
321 number_of_interfaces_to_show,
322 n,
323 array_of_prefixes_of_interfaces_to_show,
324 prefix,
325 interface,
326 label,
327 count_printed,
328 sep,
329 io_stat,
330 dw, dr,
331 bytes_per_unit\
332 ) {
333 out = ""
334 number_of_interfaces_to_show = \
335 split(\
336 opt_prefixes_of_net_interfaces_to_show,\
337 array_of_prefixes_of_interfaces_to_show,\
338 ","\
339 )
340 for (n = 1; n <= number_of_interfaces_to_show; n++) {
341 prefix = array_of_prefixes_of_interfaces_to_show[n]
342 for (interface in net_addr) {
343 if (interface ~ ("^" prefix)) {
344 label = substr(interface, 1, 1)
345 if (net_addr[interface]) {
346 bytes_per_mb = 1024 * 1024 # TODO: option
347 dw = net_io_diff_w[interface] / bytes_per_mb
348 dr = net_io_diff_r[interface] / bytes_per_mb
349 io_stat = sprintf("%0.3f▲ %0.3f▼", dw, dr)
350 } else {
351 io_stat = "--"
352 }
353 if (interface ~ "^w") {
354 label = label ":" db["net_wifi_status"]
355 }
356 if (++count_printed > 1) {
357 sep = " "
358 } else {
359 sep = ""
360 }
361 out = out sep label ":" io_stat
362 }
363 }
364 }
365 return sprintf("N[%s]", out)
366 }
367
368 function make_status_mpd( state, status) {
369 state = db["mpd_state"]
370
371 if (state == "play") {
372 status = make_status_mpd_state_known("▶")
373 } else if (state == "pause") {
374 status = make_status_mpd_state_known("❚❚")
375 } else if (state == "stop") {
376 status = make_status_mpd_state_known("⬛")
377 } else {
378 status = make_status_mpd_state_unknown("--")
379 }
380
381 return sprintf("[%s]", status)
382 }
383
384 function make_status_mpd_state_known(symbol) {
385 return sprintf(\
386 "%s %s %s %s",
387 symbol,
388 db["mpd_curr_song_time"],
389 db["mpd_curr_song_percent"],
390 substr(db["mpd_curr_song_name"], 1, opt_mpd_song_max_chars)\
391 )
392 }
393
394 function make_status_mpd_state_unknown(symbol) {
395 return sprintf("%s", symbol)
396 }
397
398 function round(n) {
399 return int(n + 0.5)
400 }
401
402 function debug(location, msg) {
403 if (opt_debug) {
404 output_msg("DEBUG", location " ==> " msg, "/dev/stderr")
405 }
406 }
This page took 0.107425 seconds and 5 git commands to generate.