From 0b29e73e2ebe45834affb53026d8029d2492fd61 Mon Sep 17 00:00:00 2001 From: Hzj_jie Date: Thu, 23 Mar 2017 23:36:15 -0700 Subject: [PATCH] BatteryStat plugin and instruction of KoboLight plugin (#2643) * Start battery stat plugin * BatteryStat & kobolight * Several minor improvements * Remove a useless function * flush settings * Some review feedbacks * Resolve review comments * Remaining Minutes -> Remaining Hours * Add dump_file * typo * realpath * realpath on folder * Remove useless os.time() * Resolve review comments * warning * Add BatteryStat.debugging flag * treat log as txt * Minor improvement * Charging hour should be positive * Use warn instead of info * onSuspend in Kobo * Charging events for kobo and kindle * More events * dumpOrLog * Warnings * Typo * More space * Singleton * slightly format change * BatteryStat singleton * Init * Remove debugging flag * sleeping percentage is still negative * Read settings * Do not need to change was_suspending and was_charging * Typo * Remove debugging flag * Not charging should happen before suspend * Resolve review comments * was_suspend and was_charging should be updated each time in onCallback() --- frontend/device/android/powerd.lua | 10 +- frontend/device/generic/powerd.lua | 29 ++- frontend/device/kindle/powerd.lua | 14 +- frontend/device/kobo/powerd.lua | 8 +- frontend/device/pocketbook/powerd.lua | 4 +- frontend/docsettings.lua | 4 - frontend/document/credocument.lua | 2 + frontend/luasettings.lua | 4 + frontend/pluginloader.lua | 5 +- frontend/ui/uimanager.lua | 19 +- plugins/batterystat.koplugin/main.lua | 283 ++++++++++++++++++++++++++ plugins/kobolight.koplugin/demo.png | Bin 0 -> 21780 bytes plugins/kobolight.koplugin/main.lua | 56 ++++- 13 files changed, 381 insertions(+), 57 deletions(-) create mode 100644 plugins/batterystat.koplugin/main.lua create mode 100644 plugins/kobolight.koplugin/demo.png diff --git a/frontend/device/android/powerd.lua b/frontend/device/android/powerd.lua index 57e50f222..faabbed73 100644 --- a/frontend/device/android/powerd.lua +++ b/frontend/device/android/powerd.lua @@ -4,10 +4,6 @@ local _, android = pcall(require, "android") local AndroidPowerD = BasePowerD:new{ fl_min = 0, fl_max = 25, fl_intensity = 10, - batt_capacity_file = "/sys/class/power_supply/battery/capacity", - is_charging_file = "/sys/class/power_supply/battery/charging_enabled", - battCapacity = nil, - is_charging = nil, } function AndroidPowerD:init() @@ -18,13 +14,11 @@ function AndroidPowerD:setIntensityHW() end function AndroidPowerD:getCapacityHW() - self.battCapacity = android.getBatteryLevel() - return self.battCapacity + return android.getBatteryLevel() end function AndroidPowerD:isChargingHW() - self.is_charging = android.isCharging() - return self.is_charging + return android.isCharging() end return AndroidPowerD diff --git a/frontend/device/generic/powerd.lua b/frontend/device/generic/powerd.lua index 6a6e52a41..ba4ac7acd 100644 --- a/frontend/device/generic/powerd.lua +++ b/frontend/device/generic/powerd.lua @@ -1,14 +1,13 @@ local logger = require("logger") local BasePowerD = { - fl_min = 0, -- min frontlight intensity - fl_max = 10, -- max frontlight intensity - fl_intensity = nil, -- frontlight intensity - battCapacity = nil, -- battery capacity - device = nil, -- device object + fl_min = 0, -- min frontlight intensity + fl_max = 10, -- max frontlight intensity + fl_intensity = nil, -- frontlight intensity + battCapacity = 0, -- battery capacity + device = nil, -- device object - capacity_pulled_count = 0, - capacity_cached_count = 10, + last_capacity_pull_time = 0, -- timestamp of last pull } function BasePowerD:new(o) @@ -22,8 +21,8 @@ end function BasePowerD:init() end function BasePowerD:toggleFrontlight() end function BasePowerD:setIntensityHW() end -function BasePowerD:getCapacityHW() return "0" end -function BasePowerD:isChargingHW() end +function BasePowerD:getCapacityHW() return 0 end +function BasePowerD:isChargingHW() return false end -- Anything needs to be done before do a real hardware suspend. Such as turn off -- front light. function BasePowerD:beforeSuspend() end @@ -66,19 +65,17 @@ function BasePowerD:setIntensity(intensity) end function BasePowerD:getCapacity() - if self.capacity_pulled_count == self.capacity_cached_count then - self.capacity_pulled_count = 0 - return self:getCapacityHW() - else - self.capacity_pulled_count = self.capacity_pulled_count + 1 - return self.battCapacity or self:getCapacityHW() + if os.time() - self.last_capacity_pull_time >= 60 then + self.battCapacity = self:getCapacityHW() + self.last_capacity_pull_time = os.time() end + return self.battCapacity end function BasePowerD:refreshCapacity() -- We want our next getCapacity call to actually pull up to date info -- instead of a cached value ;) - self.capacity_pulled_count = self.capacity_cached_count + self.last_capacity_pull_time = 0 end function BasePowerD:isCharging() diff --git a/frontend/device/kindle/powerd.lua b/frontend/device/kindle/powerd.lua index faa146cf4..b0e1a026e 100644 --- a/frontend/device/kindle/powerd.lua +++ b/frontend/device/kindle/powerd.lua @@ -5,8 +5,6 @@ local KindlePowerD = BasePowerD:new{ fl_min = 0, fl_max = 24, fl_intensity = nil, - battCapacity = nil, - is_charging = nil, lipc_handle = nil, is_fl_on = false, @@ -55,20 +53,20 @@ end function KindlePowerD:getCapacityHW() if self.lipc_handle ~= nil then - self.battCapacity = self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel") + return self.lipc_handle:get_int_property("com.lab126.powerd", "battLevel") else - self.battCapacity = self:read_int_file(self.batt_capacity_file) + return self:read_int_file(self.batt_capacity_file) end - return self.battCapacity end function KindlePowerD:isChargingHW() + local is_charging if self.lipc_handle ~= nil then - self.is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging") + is_charging = self.lipc_handle:get_int_property("com.lab126.powerd", "isCharging") else - self.is_charging = self:read_int_file(self.is_charging_file) + is_charging = self:read_int_file(self.is_charging_file) end - return self.is_charging == 1 + return is_charging == 1 end function KindlePowerD:__gc() diff --git a/frontend/device/kobo/powerd.lua b/frontend/device/kobo/powerd.lua index d52a7874a..98bcb4fae 100644 --- a/frontend/device/kobo/powerd.lua +++ b/frontend/device/kobo/powerd.lua @@ -19,8 +19,6 @@ local KoboPowerD = BasePowerD:new{ batt_capacity_file = batt_state_folder .. "capacity", is_charging_file = batt_state_folder .. "status", - battCapacity = nil, - is_charging = nil, } function KoboPowerD:init() @@ -77,13 +75,11 @@ function KoboPowerD:setIntensityHW() end function KoboPowerD:getCapacityHW() - self.battCapacity = self:read_int_file(self.batt_capacity_file) - return self.battCapacity + return self:read_int_file(self.batt_capacity_file) end function KoboPowerD:isChargingHW() - self.is_charging = self:read_str_file(self.is_charging_file) == "Charging\n" - return self.is_charging + return self:read_str_file(self.is_charging_file) == "Charging\n" end -- Turn off front light before suspend. diff --git a/frontend/device/pocketbook/powerd.lua b/frontend/device/pocketbook/powerd.lua index 49ab9d838..a1e23c171 100644 --- a/frontend/device/pocketbook/powerd.lua +++ b/frontend/device/pocketbook/powerd.lua @@ -7,7 +7,6 @@ int IsCharging(); ]] local PocketBookPowerD = BasePowerD:new{ - battCapacity = nil, is_charging = nil, batt_capacity_file = "/sys/devices/platform/sun5i-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/battery/capacity", is_charging_file = "/sys/devices/platform/sun5i-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply/battery/status", @@ -17,8 +16,7 @@ function PocketBookPowerD:init() end function PocketBookPowerD:getCapacityHW() - self.battCapacity = self:read_int_file(self.batt_capacity_file) - return self.battCapacity + return self:read_int_file(self.batt_capacity_file) end function PocketBookPowerD:isChargingHW() diff --git a/frontend/docsettings.lua b/frontend/docsettings.lua index 9b7d0a05e..87ef50752 100644 --- a/frontend/docsettings.lua +++ b/frontend/docsettings.lua @@ -2,7 +2,6 @@ local lfs = require("libs/libkoreader-lfs") local DataStorage = require("datastorage") local dump = require("dump") local purgeDir = require("ffi/util").purgeDir -local logger = require("logger") local DocSettings = {} @@ -23,9 +22,6 @@ function DocSettings:getSidecarDir(doc_path) if file_without_suffix then return file_without_suffix..".sdr" end - -- We shouldn't be called with anything but files with registered - -- extensions, but in case we are, return something useful - logger.err("getSidecarFile called with unexpected path:", doc_path) return doc_path..".sdr" end diff --git a/frontend/document/credocument.lua b/frontend/document/credocument.lua index 69e729612..73d204a4f 100644 --- a/frontend/document/credocument.lua +++ b/frontend/document/credocument.lua @@ -459,7 +459,9 @@ end function CreDocument:register(registry) registry:addProvider("txt", "application/txt", self) + registry:addProvider("log", "application/txt", self) registry:addProvider("txt.zip", "application/zip", self) + registry:addProvider("log.zip", "application/zip", self) registry:addProvider("epub", "application/epub", self) registry:addProvider("fb2", "application/fb2", self) registry:addProvider("fb2.zip", "application/zip", self) diff --git a/frontend/luasettings.lua b/frontend/luasettings.lua index bd8629b7c..a08707329 100644 --- a/frontend/luasettings.lua +++ b/frontend/luasettings.lua @@ -60,6 +60,10 @@ function LuaSettings:flipTrue(key) end end +function LuaSettings:reset(table) + self.data = table +end + function LuaSettings:flush() local f_out = io.open(self.file, "w") if f_out ~= nil then diff --git a/frontend/pluginloader.lua b/frontend/pluginloader.lua index 26e300179..fa39a3352 100644 --- a/frontend/pluginloader.lua +++ b/frontend/pluginloader.lua @@ -22,13 +22,14 @@ function PluginLoader:loadPlugins() local ok, plugin_module = pcall(dofile, mainfile) if not ok or not plugin_module then logger.warn("Error when loading", mainfile, plugin_module) - end - if ok and plugin_module and not plugin_module.disabled then + elseif type(plugin_module.disabled) ~= "boolean" or not plugin_module.disabled then package.path = package_path package.cpath = package_cpath plugin_module.path = path plugin_module.name = plugin_module.name or path:match("/(.-)%.koplugin") table.insert(self.plugins, plugin_module) + else + logger.info("Plugin ", mainfile, " has been disabled.") end end end diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 0138f98bc..889f09be6 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -60,11 +60,12 @@ function UIManager:init() self:_initAutoSuspend() self.event_handlers["Suspend"] = function() self:_stopAutoSuspend() + self:broadcastEvent(Event:new("Suspend")) Device:onPowerEvent("Suspend") end self.event_handlers["Resume"] = function() Device:onPowerEvent("Resume") - self:sendEvent(Event:new("Resume")) + self:broadcastEvent(Event:new("Resume")) self:_startAutoSuspend() end self.event_handlers["PowerPress"] = function() @@ -99,11 +100,17 @@ function UIManager:init() Device:getPowerDevice():toggleFrontlight() end self.event_handlers["Charging"] = function() + self:broadcastEvent(Event:new("Charging")) + if Device.screen_saver_mode then + self.event_handlers["Suspend"]() + end + end + self.event_handlers["NotCharging"] = function() + self:broadcastEvent(Event:new("NotCharging")) if Device.screen_saver_mode then self.event_handlers["Suspend"]() end end - self.event_handlers["NotCharging"] = self.event_handlers["Charging"] self.event_handlers["__default__"] = function(input_event) if Device.screen_saver_mode then -- Suspension in Kobo can be interrupted by screen updates. We @@ -116,18 +123,20 @@ function UIManager:init() end elseif Device:isKindle() then self.event_handlers["IntoSS"] = function() + self:broadcastEvent(Event:new("Suspend")) Device:intoScreenSaver() end self.event_handlers["OutOfSS"] = function() Device:outofScreenSaver() - self:sendEvent(Event:new("Resume")) + self:broadcastEvent(Event:new("Resume")) end self.event_handlers["Charging"] = function() + self:broadcastEvent(Event:new("Charging")) Device:usbPlugIn() end self.event_handlers["NotCharging"] = function() Device:usbPlugOut() - self:sendEvent(Event:new("NotCharging")) + self:broadcastEvent(Event:new("NotCharging")) end end end @@ -728,7 +737,7 @@ function UIManager:_initAutoSuspend() local now = util.gettime() -- Do not repeat auto suspend procedure after suspend. if self.last_action_sec + self.auto_suspend_sec <= now then - Device:onPowerEvent("Suspend") + self.event_handlers["Suspend"]() else self:scheduleIn( self.last_action_sec + self.auto_suspend_sec - now, diff --git a/plugins/batterystat.koplugin/main.lua b/plugins/batterystat.koplugin/main.lua new file mode 100644 index 000000000..d8217835a --- /dev/null +++ b/plugins/batterystat.koplugin/main.lua @@ -0,0 +1,283 @@ + +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 T = require("ffi/util").template +local logger = require("logger") +local util = require("ffi/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:getCapacity() + 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 = {} + +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 + (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() + local curr = State:new() + return curr.percentage / self:percentagePerHour() +end + +function Usage:chargingHours() + local curr = State:new() + return (curr.percentage - 100) / self:percentagePerHour() +end + +local function shorten(number) + return string.format("%.2f", number); +end + +function Usage:dump(kv_pairs) + table.insert(kv_pairs, {_(" Consumed %"), shorten(self.percentage)}) + table.insert(kv_pairs, {_(" Total minutes"), shorten(self:minutes())}) + table.insert(kv_pairs, {_(" % per hour"), shorten(self:percentagePerHour())}) +end + +function Usage:dumpRemaining(kv_pairs) + table.insert(kv_pairs, {_(" Estimated remaining hours"), shorten(self:remainingHours())}) +end + +function Usage:dumpCharging(kv_pairs) + table.insert(kv_pairs, {_(" Estimated hours for charging"), shorten(self:chargingHours())}) +end + +local BatteryStat = { + name = "batterstat", + settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/batterstat.lua"), + dump_file = util.realpath(DataStorage:getDataDir()) .. "/batterystat.log", + debugging = false, +} + +function BatteryStat:init() + self.charging = Usage:new(self.settings:readSetting("charging")) + self.decharging = Usage:new(self.settings:readSetting("decharging")) + self.awake = Usage:new(self.settings:readSetting("awake")) + self.sleeping = Usage:new(self.settings:readSetting("sleeping")) + + -- Note: these fields are not the "real" timestamp and battery usage, but + -- the unaccumulated values. + self.charging_state = State:new(self.settings:readSetting("charging_state")) + self.awake_state = State:new(self.settings:readSetting("awake_state")) + self:initCurrentState() + + if self.debugging then + self.debugOutput = self._debugOutput + else + self.debugOutput = function() end + end +end + +function BatteryStat:initCurrentState() + -- 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() +end + +function BatteryStat:onFlushSettings() + self.settings:reset({ + charging = self.charging, + decharging = self.decharging, + 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 then + -- Suspending to awake. + self.sleeping:append(self.awake_state) + else + -- 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.decharging:append(self.charging_state) + end + self.awake_state = State:new() + self.charging_state = State:new() +end + +function BatteryStat:dumpOrLog(content) + local file = io.open(self.dump_file, "a") + if file then + file:write(content .. "\n") + file:close() + else + logger.warn("Failed to dump output ", content, " into ", self.dump_file ) + end +end + +function BatteryStat:_debugOutput(event) + self:dumpOrLog(event .. " @ " .. State:new():toString() .. + ", awake_state " .. self.awake_state:toString() .. + ", charging_state " .. self.charging_state:toString()) +end + +function BatteryStat:onSuspend() + self:debugOutput("onSuspend") + self.was_suspending = false + self:accumulate() +end + +function BatteryStat:onResume() + self:debugOutput("onResume") + self.was_suspending = true + self:accumulate() +end + +function BatteryStat:onCharging() + self:debugOutput("onCharging") + self.was_charging = false + self:dumpToText() + self.charging = Usage:new() + self.awake = Usage:new() + self.sleeping = Usage:new() + self:accumulate() +end + +function BatteryStat:onNotCharging() + self:debugOutput("onNotCharging") + self.was_charging = true + self:dumpToText() + self.decharging = Usage:new() + self.awake = Usage:new() + self.sleeping = Usage:new() + self:accumulate() +end + +function BatteryStat:onCallback() + self:initCurrentState() + self:accumulate() + local kv_pairs = self:dump() + table.insert(kv_pairs, "----------") + table.insert(kv_pairs, {_("Historical records are dumped to"), ""}) + table.insert(kv_pairs, {self.dump_file, ""}) + UIManager:show(KeyValuePage:new{ + title = _("Battery statistics"), + kv_pairs = kv_pairs, + }) +end + +function BatteryStat:dumpToText() + local kv_pairs = self:dump() + local content = T(_("Dump at %1"), os.date("%c")) + for _, pair in ipairs(kv_pairs) do + content = content .. "\n" .. pair[1] + if pair[2] ~= nil and pair[2] ~= "" then + content = content .. "\t" .. pair[2] + end + end + self:dumpOrLog(content .. "\n-=-=-=-=-=-\n") +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.decharging:dump(kv_pairs) + self.decharging:dumpRemaining(kv_pairs) + return kv_pairs +end + +BatteryStat:init() + +local BatteryStatWidget = WidgetContainer:new() + +function BatteryStatWidget:init() + self.ui.menu:registerToMainMenu(self) +end + +function BatteryStatWidget:addToMainMenu(tab_item_table) + table.insert(tab_item_table.plugins, { + text = _("Battery statistics"), + callback = function() + BatteryStat:onCallback() + 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 + +return BatteryStatWidget diff --git a/plugins/kobolight.koplugin/demo.png b/plugins/kobolight.koplugin/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..61b76f38fc71f7014e6247803b7b1bef01537c91 GIT binary patch literal 21780 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 vC*QuY5%