executable_schedule-power 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env -S guile --no-auto-compile -e main -s
  2. !#
  3. (use-modules (ice-9 match)
  4. (ice-9 popen)
  5. (ice-9 rdelim)
  6. (srfi srfi-1)
  7. (srfi srfi-19))
  8. (define %sudo-binary
  9. "sudo")
  10. (define %halt-binary
  11. "halt")
  12. (define (off-time?)
  13. "Return true if off-time, false otherwise."
  14. (let* ((current (current-date))
  15. (current-hour (date-hour current)))
  16. (or (> (date-week-day current) 5)
  17. (<= current-hour 10)
  18. (>= current-hour 18))))
  19. ;;;
  20. ;;; Mouse
  21. ;;;
  22. (define %xdotool-binary
  23. "xdotool")
  24. (define (mouse-position)
  25. "Return current mouse position."
  26. (let* ((port (open-pipe* OPEN_READ %xdotool-binary "getmouselocation"))
  27. (output (read-string port)))
  28. (close-port port)
  29. (map (lambda (str)
  30. (match (string-split str #\:)
  31. ((key value)
  32. (cons (string->symbol key)
  33. (string->number value)))))
  34. (string-split (string-trim-right output #\newline)
  35. #\space))))
  36. (define (mouse-idle?)
  37. "Return true if mouse if idle, false otherwise."
  38. (define %mouse-position-count
  39. (or (getenv "AUTO_SHUTDOWN_MOUSE_POSITION")
  40. 2))
  41. (define %mouse-position-interval
  42. (or (getenv "AUTO_SHUTDOWN_MOUSE_POSITION_INTERVAL")
  43. 2))
  44. (define %mouse-attempt
  45. (or (getenv "AUTO_SHUTDOWN_MOUSE_ATTEMPT")
  46. 8))
  47. (let loop ((attempt 0)
  48. (value-parameter '())
  49. (value-count 0))
  50. (let ((mouse-idle? (>= value-count %mouse-position-count)))
  51. (if (or mouse-idle?
  52. (>= attempt %mouse-attempt))
  53. mouse-idle?
  54. (begin
  55. (let ((current-value (mouse-position)))
  56. (sleep %mouse-position-interval)
  57. (loop (1+ attempt)
  58. current-value
  59. (if (equal? value-parameter current-value)
  60. (1+ value-count)
  61. 0))))))))
  62. ;;;
  63. ;;; Monitor
  64. ;;;
  65. (define %xset-binary
  66. "xset")
  67. (define (monitor-on?)
  68. "Return true if monitor is on, or false if monitor if off."
  69. (let* ((port (open-pipe* OPEN_READ %xset-binary "q"))
  70. (output (read-string port)))
  71. (close-port port)
  72. (match (last
  73. (string-split
  74. (last
  75. (string-split (string-trim-right output #\newline)
  76. #\newline))
  77. #\space))
  78. ("On" #true)
  79. ("Off" #false))))
  80. (define (monitor-idle?)
  81. "Return true if monitor if idle, false otherwise."
  82. (define %monitor-count
  83. (or (getenv "AUTO_SHUTDOWN_MONITOR_COUNT")
  84. 2))
  85. (define %monitor-interval
  86. (or (getenv "AUTO_SHUTDOWN_MONITOR_INTERVAL")
  87. 2))
  88. (define %monitor-attempt
  89. (or (getenv "AUTO_SHUTDOWN_MONITOR_ATTEMPT")
  90. 8))
  91. (define (monitor-off?)
  92. (not (monitor-on?)))
  93. (let loop ((attempt 0)
  94. (value-count 0))
  95. (let ((monitor-idle? (>= value-count %monitor-count)))
  96. (if (or monitor-idle?
  97. (>= attempt %monitor-attempt))
  98. monitor-idle?
  99. (begin
  100. (let ((current-value (monitor-off?)))
  101. (sleep %monitor-interval)
  102. (loop (1+ attempt)
  103. (if current-value
  104. (1+ value-count)
  105. 0))))))))
  106. ;;;
  107. ;;; Audio
  108. ;;;
  109. (define (audio-off?)
  110. "Return true if audio is off, false otherwise."
  111. (let* ((port (open-pipe "parec --raw --channels=1 --latency=2 2>/dev/null | od -N2 -td2 | head -n1 | cut -d' ' -f2- | tr -d ' '"
  112. OPEN_READ))
  113. (output (read-string port)))
  114. (close-port port)
  115. (= (string->number (string-trim-right output #\newline))
  116. 0)))
  117. (define (audio-idle?)
  118. "Return true if audio if idle, false otherwise."
  119. (define %audio-count
  120. (or (getenv "AUTO_SHUTDOWN_AUDIO_COUNT")
  121. 5))
  122. (define %audio-interval
  123. (or (getenv "AUTO_SHUTDOWN_AUDIO_INTERVAL")
  124. 2))
  125. (define %audio-attempt
  126. (or (getenv "AUTO_SHUTDOWN_AUDIO_ATTEMPT")
  127. 8))
  128. (let loop ((attempt 0)
  129. (value-parameter '())
  130. (value-count 0))
  131. (let ((audio-idle? (>= value-count %audio-count)))
  132. (if (or audio-idle?
  133. (>= attempt %audio-attempt))
  134. audio-idle?
  135. (begin
  136. (let* ((current-value (audio-off?))
  137. (value-parameter (append value-parameter
  138. (list current-value))))
  139. (sleep %audio-interval)
  140. (loop (1+ attempt)
  141. value-parameter
  142. (if (every (lambda (value) (eq? value #t)) value-parameter)
  143. (1+ value-count)
  144. 0))))))))
  145. ;;;
  146. ;;; Processes
  147. ;;;
  148. (define %pgrep-binary
  149. "pgrep")
  150. (define (process-missing? process-name)
  151. (with-output-to-port (open-output-file "/dev/null")
  152. (lambda ()
  153. (= (system* %pgrep-binary "--full" "--list-full" process-name)
  154. 256))))
  155. ;;;
  156. ;;; Entry point
  157. ;;;
  158. (define (main . args)
  159. (if (and (off-time?)
  160. (process-missing? "bin/restic")
  161. (and (monitor-idle?) (audio-idle?))
  162. (mouse-idle?))
  163. (system* %sudo-binary %halt-binary)
  164. (exit 1)))