My configuration files for Debian/Ubuntu applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bat.lua 9.0 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. --[[
  2. Licensed under GNU General Public License v2
  3. * (c) 2013, Luca CPZ
  4. * (c) 2010-2012, Peter Hofmann
  5. --]]
  6. local helpers = require("lain.helpers")
  7. local fs = require("gears.filesystem")
  8. local naughty = require("naughty")
  9. local wibox = require("wibox")
  10. local math = math
  11. local string = string
  12. local ipairs = ipairs
  13. local tonumber = tonumber
  14. -- Battery infos
  15. -- lain.widget.bat
  16. local function factory(args)
  17. local pspath = args.pspath or "/sys/class/power_supply/"
  18. if not fs.is_dir(pspath) then
  19. naughty.notify { text = "lain.widget.bat: invalid power supply path", timeout = 0 }
  20. return
  21. end
  22. args = args or {}
  23. local bat = { widget = args.widget or wibox.widget.textbox() }
  24. local timeout = args.timeout or 30
  25. local notify = args.notify or "on"
  26. local full_notify = args.full_notify or notify
  27. local n_perc = args.n_perc or { 5, 15 }
  28. local batteries = args.batteries or (args.battery and {args.battery}) or {}
  29. local ac = args.ac or "AC0"
  30. local settings = args.settings or function() end
  31. function bat.get_batteries()
  32. helpers.line_callback("ls -1 " .. pspath, function(line)
  33. local bstr = string.match(line, "BAT%w+")
  34. if bstr then
  35. batteries[#batteries + 1] = bstr
  36. else
  37. ac = string.match(line, "A%w+") or ac
  38. end
  39. end)
  40. end
  41. if #batteries == 0 then bat.get_batteries() end
  42. bat_notification_critical_preset = {
  43. title = "Battery exhausted",
  44. text = "Shutdown imminent",
  45. timeout = 15,
  46. fg = "#000000",
  47. bg = "#FFFFFF"
  48. }
  49. bat_notification_low_preset = {
  50. title = "Battery low",
  51. text = "Plug the cable!",
  52. timeout = 15,
  53. fg = "#202020",
  54. bg = "#CDCDCD"
  55. }
  56. bat_notification_charged_preset = {
  57. title = "Battery full",
  58. text = "You can unplug the cable",
  59. timeout = 15,
  60. fg = "#202020",
  61. bg = "#CDCDCD"
  62. }
  63. bat_now = {
  64. status = "N/A",
  65. ac_status = "N/A",
  66. perc = "N/A",
  67. time = "N/A",
  68. watt = "N/A",
  69. capacity = "N/A"
  70. }
  71. bat_now.n_status = {}
  72. bat_now.n_perc = {}
  73. bat_now.n_capacity = {}
  74. for i = 1, #batteries do
  75. bat_now.n_status[i] = "N/A"
  76. bat_now.n_perc[i] = 0
  77. bat_now.n_capacity[i] = 0
  78. end
  79. -- used to notify full charge only once before discharging
  80. local fullnotification = false
  81. function bat.update()
  82. -- luacheck: globals bat_now
  83. local sum_rate_current = 0
  84. local sum_rate_voltage = 0
  85. local sum_rate_power = 0
  86. local sum_rate_energy = 0
  87. local sum_energy_now = 0
  88. local sum_energy_full = 0
  89. local sum_charge_full = 0
  90. local sum_charge_design = 0
  91. for i, battery in ipairs(batteries) do
  92. local bstr = pspath .. battery
  93. local present = helpers.first_line(bstr .. "/present")
  94. if tonumber(present) == 1 then
  95. -- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
  96. local rate_current = tonumber(helpers.first_line(bstr .. "/current_now"))
  97. local rate_voltage = tonumber(helpers.first_line(bstr .. "/voltage_now"))
  98. local rate_power = tonumber(helpers.first_line(bstr .. "/power_now"))
  99. local charge_full = tonumber(helpers.first_line(bstr .. "/charge_full"))
  100. local charge_design = tonumber(helpers.first_line(bstr .. "/charge_full_design"))
  101. -- energy_now(P)[uWh], charge_now(I)[uAh]
  102. local energy_now = tonumber(helpers.first_line(bstr .. "/energy_now") or
  103. helpers.first_line(bstr .. "/charge_now"))
  104. -- energy_full(P)[uWh], charge_full(I)[uAh]
  105. local energy_full = tonumber(helpers.first_line(bstr .. "/energy_full") or
  106. charge_full)
  107. local energy_percentage = tonumber(helpers.first_line(bstr .. "/capacity")) or
  108. math.floor((energy_now / energy_full) * 100)
  109. bat_now.n_status[i] = helpers.first_line(bstr .. "/status") or "N/A"
  110. bat_now.n_perc[i] = energy_percentage or bat_now.n_perc[i]
  111. if not charge_design or charge_design == 0 then
  112. bat_now.n_capacity[i] = 0
  113. else
  114. bat_now.n_capacity[i] = math.floor((charge_full / charge_design) * 100)
  115. end
  116. sum_rate_current = sum_rate_current + (rate_current or 0)
  117. sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0)
  118. sum_rate_power = sum_rate_power + (rate_power or 0)
  119. sum_rate_energy = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
  120. sum_energy_now = sum_energy_now + (energy_now or 0)
  121. sum_energy_full = sum_energy_full + (energy_full or 0)
  122. sum_charge_full = sum_charge_full + (charge_full or 0)
  123. sum_charge_design = sum_charge_design + (charge_design or 0)
  124. end
  125. end
  126. bat_now.capacity = math.floor(math.min(100, (sum_charge_full / sum_charge_design) * 100))
  127. -- When one of the battery is charging, others' status are either
  128. -- "Full", "Unknown" or "Charging". When the laptop is not plugged in,
  129. -- one or more of the batteries may be full, but only one battery
  130. -- discharging suffices to set global status to "Discharging".
  131. bat_now.status = bat_now.n_status[1] or "N/A"
  132. for _,status in ipairs(bat_now.n_status) do
  133. if status == "Discharging" or status == "Charging" then
  134. bat_now.status = status
  135. end
  136. end
  137. bat_now.ac_status = tonumber(helpers.first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
  138. if bat_now.status ~= "N/A" then
  139. if bat_now.status ~= "Full" and sum_rate_power == 0 and bat_now.ac_status == 1 then
  140. bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
  141. bat_now.time = "00:00"
  142. bat_now.watt = 0
  143. -- update {perc,time,watt} iff battery not full and rate > 0
  144. elseif bat_now.status ~= "Full" then
  145. local rate_time = 0
  146. -- Calculate time and watt if rates are greater then 0
  147. if (sum_rate_power > 0 or sum_rate_current > 0) then
  148. local div = (sum_rate_power > 0 and sum_rate_power) or sum_rate_current
  149. if bat_now.status == "Charging" then
  150. rate_time = (sum_energy_full - sum_energy_now) / div
  151. else -- Discharging
  152. rate_time = sum_energy_now / div
  153. end
  154. if 0 < rate_time and rate_time < 0.01 then -- check for magnitude discrepancies (#199)
  155. rate_time_magnitude = math.abs(math.floor(math.log10(rate_time)))
  156. rate_time = rate_time * 10^(rate_time_magnitude - 2)
  157. end
  158. end
  159. local hours = math.floor(rate_time)
  160. local minutes = math.floor((rate_time - hours) * 60)
  161. bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100))
  162. bat_now.time = string.format("%02d:%02d", hours, minutes)
  163. bat_now.watt = tonumber(string.format("%.2f", sum_rate_energy / 1e6))
  164. elseif bat_now.status == "Full" then
  165. bat_now.perc = 100
  166. bat_now.time = "00:00"
  167. bat_now.watt = 0
  168. end
  169. end
  170. widget = bat.widget
  171. settings()
  172. -- notifications for critical, low, and full levels
  173. if notify == "on" then
  174. if bat_now.status == "Discharging" then
  175. if tonumber(bat_now.perc) <= n_perc[1] then
  176. bat.id = naughty.notify({
  177. preset = bat_notification_critical_preset,
  178. replaces_id = bat.id
  179. }).id
  180. elseif tonumber(bat_now.perc) <= n_perc[2] then
  181. bat.id = naughty.notify({
  182. preset = bat_notification_low_preset,
  183. replaces_id = bat.id
  184. }).id
  185. end
  186. fullnotification = false
  187. elseif bat_now.status == "Full" and full_notify == "on" and not fullnotification then
  188. bat.id = naughty.notify({
  189. preset = bat_notification_charged_preset,
  190. replaces_id = bat.id
  191. }).id
  192. fullnotification = true
  193. end
  194. end
  195. end
  196. helpers.newtimer("batteries", timeout, bat.update)
  197. return bat
  198. end
  199. return factory