mirror of
https://github.com/koreader/koreader
synced 2024-11-10 01:10:34 +00:00
830fc790f1
- fix incorrectly shown awake, sleeping, charging and discharging, - remove unneeded debug mode and logging to external file, - prevent showing values like inf, -inf, nan in estimated times (we now show "n/a"), and values below zero, - show extra confirm box when we want to reset data, - show time in format xxhxxm instead of only pure minutes, - check at initialization that the device was charging when it was turned off (battery level larger than at the time of exit).
313 lines
8.9 KiB
Lua
313 lines
8.9 KiB
Lua
local ConfirmBox = require("ui/widget/confirmbox")
|
|
local DataStorage = require("datastorage")
|
|
local KeyValuePage = require("ui/widget/keyvaluepage")
|
|
local LuaSettings = require("luasettings")
|
|
local PowerD = require("device"):getPowerDevice()
|
|
local UIManager = require("ui/uimanager")
|
|
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
|
local dbg = require("dbg")
|
|
local util = require("util")
|
|
local _ = require("gettext")
|
|
|
|
local State = {}
|
|
|
|
function State:new(o)
|
|
o = o or {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if o.percentage == nil or o.timestamp == nil then
|
|
o.percentage = PowerD:getCapacityHW()
|
|
o.timestamp = os.time()
|
|
end
|
|
return o
|
|
end
|
|
|
|
function State:toString()
|
|
return string.format("{%d @ %s}", self.percentage, os.date("%c", self.timestamp))
|
|
end
|
|
|
|
local Usage = {}
|
|
local INDENTATION = " " -- Three spaces.
|
|
|
|
function Usage:new(o)
|
|
o = o or {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if o.percentage == nil or o.time == nil then
|
|
o.percentage = 0
|
|
o.time = 0
|
|
end
|
|
return o
|
|
end
|
|
|
|
function Usage:append(state)
|
|
local curr = State:new()
|
|
self.percentage = self.percentage + math.abs(state.percentage - curr.percentage)
|
|
self.time = self.time + os.difftime(curr.timestamp - state.timestamp)
|
|
end
|
|
|
|
function Usage:minutes()
|
|
return self.time / 60
|
|
end
|
|
|
|
function Usage:hours()
|
|
return self:minutes() / 60
|
|
end
|
|
|
|
function Usage:percentagePerHour()
|
|
if self.time == 0 then
|
|
return 0
|
|
else
|
|
return self.percentage / self:hours()
|
|
end
|
|
end
|
|
|
|
function Usage:remainingHours()
|
|
if self:percentagePerHour() == 0 then return "n/a" end
|
|
local curr = State:new()
|
|
return curr.percentage / self:percentagePerHour()
|
|
end
|
|
|
|
function Usage:chargingHours()
|
|
if self:percentagePerHour() == 0 then return "n/a" end
|
|
local curr = State:new()
|
|
return math.abs(curr.percentage - 100) / self:percentagePerHour()
|
|
end
|
|
|
|
local function shorten(number)
|
|
if number == "n/a" then return _("n/a") end
|
|
return string.format("%.2f", number);
|
|
end
|
|
|
|
function Usage:dump(kv_pairs)
|
|
table.insert(kv_pairs, {INDENTATION .. _("Consumed %"), shorten(self.percentage)})
|
|
table.insert(kv_pairs, {INDENTATION .. _("Total time"), util.secondsToHClock(self.time, true, true)})
|
|
table.insert(kv_pairs, {INDENTATION .. _("% per hour"), shorten(self:percentagePerHour())})
|
|
end
|
|
|
|
function Usage:dumpRemaining(kv_pairs)
|
|
table.insert(kv_pairs, {INDENTATION .. _("Estimated remaining hours"), shorten(self:remainingHours())})
|
|
end
|
|
|
|
function Usage:dumpCharging(kv_pairs)
|
|
table.insert(kv_pairs, {INDENTATION .. _("Estimated hours for charging"), shorten(self:chargingHours())})
|
|
end
|
|
|
|
local BatteryStat = {
|
|
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/battery_stats.lua"),
|
|
kv_page = nil,
|
|
}
|
|
|
|
function BatteryStat:init()
|
|
self.awake = Usage:new(self.settings:readSetting("awake"))
|
|
self.sleeping = Usage:new(self.settings:readSetting("sleeping"))
|
|
self.charging = Usage:new(self.settings:readSetting("charging"))
|
|
self.discharging = Usage:new(self.settings:readSetting("discharging"))
|
|
|
|
-- Note: these fields are not the "real" timestamp and battery usage, but
|
|
-- the unaccumulated values.
|
|
self.awake_state = State:new()
|
|
self.charging_state = State:new()
|
|
|
|
-- Whether the device was suspending before current timestamp.
|
|
self.was_suspending = false
|
|
-- Whether the device was charging before current timestamp.
|
|
self.was_charging = PowerD:isCharging()
|
|
if self.was_charging then
|
|
self:reset(true, false)
|
|
end
|
|
-- Check if the battery was charging when KO was turned off.
|
|
local battery_before_off = self.settings:readSetting("awake_state")
|
|
if battery_before_off and battery_before_off.percentage
|
|
and self.awake_state.percentage > battery_before_off.percentage then
|
|
self:reset(false, true)
|
|
end
|
|
end
|
|
|
|
function BatteryStat:onFlushSettings()
|
|
self.settings:reset({
|
|
charging = self.charging,
|
|
discharging = self.discharging,
|
|
awake = self.awake,
|
|
sleeping = self.sleeping,
|
|
charging_state = self.charging_state,
|
|
awake_state = self.awake_state,
|
|
})
|
|
self.settings:flush()
|
|
end
|
|
|
|
function BatteryStat:accumulate()
|
|
if self.was_suspending and not self.was_charging then
|
|
-- Suspending to awake.
|
|
self.sleeping:append(self.awake_state)
|
|
elseif not self.was_suspending and not self.was_charging then
|
|
-- Awake to suspending, time between self.awake_state and now should belong to awake.
|
|
self.awake:append(self.awake_state)
|
|
end
|
|
if self.was_charging then
|
|
-- Decharging to charging.
|
|
self.charging:append(self.charging_state)
|
|
else
|
|
self.discharging:append(self.charging_state)
|
|
end
|
|
self.awake_state = State:new()
|
|
self.charging_state = State:new()
|
|
end
|
|
|
|
function BatteryStat:onSuspend()
|
|
if not self.was_suspending then
|
|
self:accumulate()
|
|
end
|
|
self.was_suspending = true
|
|
end
|
|
|
|
function BatteryStat:onResume()
|
|
if self.was_suspending then
|
|
self:accumulate()
|
|
end
|
|
self.was_suspending = false
|
|
end
|
|
|
|
function BatteryStat:onCharging()
|
|
if not self.was_charging then
|
|
self:reset(true, true)
|
|
self:accumulate()
|
|
end
|
|
self.was_charging = true
|
|
end
|
|
|
|
function BatteryStat:onNotCharging()
|
|
if self.was_charging then
|
|
self:reset(false, true)
|
|
self:accumulate()
|
|
end
|
|
self.was_charging = false
|
|
end
|
|
|
|
function BatteryStat:showStatistics()
|
|
local function askResetData()
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("Are you sure that you want to clear battery statistics?"),
|
|
ok_text = _("Clear"),
|
|
ok_callback = function()
|
|
self:resetAll()
|
|
self:restart()
|
|
end,
|
|
})
|
|
end
|
|
|
|
self:accumulate()
|
|
local kv_pairs = self:dump()
|
|
table.insert(kv_pairs, "----------")
|
|
table.insert(kv_pairs, {_("If you would like to reset the data,"), "",
|
|
callback = function()
|
|
UIManager:setDirty(self.kv_page, "fast")
|
|
UIManager:scheduleIn(0.1, function()
|
|
askResetData()
|
|
end)
|
|
end})
|
|
table.insert(kv_pairs, {_("please tap here."), "",
|
|
callback = function()
|
|
UIManager:setDirty(self.kv_page, "fast")
|
|
UIManager:scheduleIn(0.1, function()
|
|
askResetData()
|
|
end)
|
|
end})
|
|
self.kv_page = KeyValuePage:new{
|
|
title = _("Battery statistics"),
|
|
kv_pairs = kv_pairs,
|
|
}
|
|
UIManager:show(self.kv_page)
|
|
end
|
|
|
|
function BatteryStat:reset(withCharging, withDischarging)
|
|
self.awake = Usage:new()
|
|
self.sleeping = Usage:new()
|
|
|
|
if withCharging then
|
|
self.charging = Usage:new()
|
|
end
|
|
if withDischarging then
|
|
self.discharging = Usage:new()
|
|
end
|
|
self.awake_state = State:new()
|
|
end
|
|
|
|
function BatteryStat:resetAll()
|
|
self:reset(true, true)
|
|
self.charging_state = State:new()
|
|
self.awake_state = State:new()
|
|
end
|
|
|
|
function BatteryStat:restart()
|
|
dbg.dassert(self.kv_page ~= nil)
|
|
UIManager:close(self.kv_page)
|
|
self:showStatistics()
|
|
end
|
|
|
|
function BatteryStat:dump()
|
|
local kv_pairs = {}
|
|
table.insert(kv_pairs, {_("Awake since last charge"), ""})
|
|
self.awake:dump(kv_pairs)
|
|
self.awake:dumpRemaining(kv_pairs)
|
|
table.insert(kv_pairs, {_("Sleeping since last charge"), ""})
|
|
self.sleeping:dump(kv_pairs)
|
|
self.sleeping:dumpRemaining(kv_pairs)
|
|
table.insert(kv_pairs, {_("During last charge"), ""})
|
|
self.charging:dump(kv_pairs)
|
|
self.charging:dumpCharging(kv_pairs)
|
|
table.insert(kv_pairs, {_("Since last charge"), ""})
|
|
self.discharging:dump(kv_pairs)
|
|
self.discharging:dumpRemaining(kv_pairs)
|
|
return kv_pairs
|
|
end
|
|
|
|
BatteryStat:init()
|
|
|
|
local BatteryStatWidget = WidgetContainer:new{
|
|
name = "batterystat",
|
|
}
|
|
|
|
function BatteryStatWidget:init()
|
|
-- self.ui is nil in test cases.
|
|
if not self.ui or not self.ui.menu then return end
|
|
self.ui.menu:registerToMainMenu(self)
|
|
end
|
|
|
|
function BatteryStatWidget:addToMainMenu(menu_items)
|
|
menu_items.battery_statistics = {
|
|
text = _("Battery statistics"),
|
|
keep_menu_open = true,
|
|
callback = function()
|
|
BatteryStat:showStatistics()
|
|
end,
|
|
}
|
|
end
|
|
|
|
function BatteryStatWidget:onFlushSettings()
|
|
BatteryStat:onFlushSettings()
|
|
end
|
|
|
|
function BatteryStatWidget:onSuspend()
|
|
BatteryStat:onSuspend()
|
|
end
|
|
|
|
function BatteryStatWidget:onResume()
|
|
BatteryStat:onResume()
|
|
end
|
|
|
|
function BatteryStatWidget:onCharging()
|
|
BatteryStat:onCharging()
|
|
end
|
|
|
|
function BatteryStatWidget:onNotCharging()
|
|
BatteryStat:onNotCharging()
|
|
end
|
|
|
|
-- Test only
|
|
function BatteryStatWidget:stat()
|
|
return BatteryStat
|
|
end
|
|
|
|
return BatteryStatWidget
|