mirror of https://github.com/koreader/koreader
PocketBook: Auto-standby plugin (#6602)
Adaptively pins the auto-standby UI lock. When frequent input from user is observed, we'll prevent for increasingly longer periods of time, and revert back to more aggressive standby with infrequent input (infrequent taps when reading a book).pull/6612/head
parent
962fd02c98
commit
724d3aa5ff
@ -0,0 +1,7 @@
|
||||
local _ = require("gettext")
|
||||
return {
|
||||
name = "autostandby",
|
||||
fullname = _("Auto Standby"),
|
||||
description = _([[Put into standby on no input, wake up from standby on UI input]]),
|
||||
sorting_hint = "device",
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
local Device = require("device")
|
||||
|
||||
if not Device:isPocketBook() --[[and not Device:isKobo()]] then
|
||||
return { disabled = true }
|
||||
end
|
||||
|
||||
local PowerD = Device:getPowerDevice()
|
||||
local DataStorage = require("datastorage")
|
||||
local LuaSettings = require("luasettings")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local SpinWidget = require("ui/widget/spinwidget")
|
||||
local logger = require("logger")
|
||||
local _ = require("gettext")
|
||||
|
||||
local AutoStandby = WidgetContainer:new{
|
||||
is_doc_only = false,
|
||||
name = "autostandby",
|
||||
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/autostandby.lua"),
|
||||
delay = 0,
|
||||
lastInput = 0,
|
||||
preventing = false,
|
||||
}
|
||||
|
||||
function AutoStandby:init()
|
||||
if not self.settings:has("filter") then
|
||||
logger.dbg("AutoStandby: No settings found, initializing defaults")
|
||||
self.settings.data = {
|
||||
forbidden = false, -- If forbidden, standby is never allowed to occur
|
||||
filter = 1, -- Consider input only further than this many seconds apart
|
||||
min = 1, -- Initial delay period during which we won't standby
|
||||
mul = 1.5, -- Multiply the delay with each subsequent input that happens, scales up to max
|
||||
max = 30, -- Input that happens further than 30 seconds since last input one reset delay back to 'min'
|
||||
win = 5, -- Additional time window to consider input contributing to standby delay
|
||||
bat = 60, -- If battery is below this percent, make auto-standby aggressive again (disables scaling by mul)
|
||||
}
|
||||
self.settings:flush()
|
||||
end
|
||||
|
||||
UIManager.event_hook:registerWidget("InputEvent", self)
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
function AutoStandby:addToMainMenu(menu_items)
|
||||
menu_items.autostandby = {
|
||||
text = _("Auto-standby settings"),
|
||||
sub_item_table = {
|
||||
{
|
||||
keep_menu_open = true,
|
||||
text = _("Allow auto-standby"),
|
||||
checked_func = function() return self:isAllowedByConfig() end,
|
||||
callback = function() self.settings:saveSetting("forbidden", self:isAllowedByConfig()):flush() end,
|
||||
},
|
||||
self:genSpinMenuItem(_("Min input idle seconds"), "min", function() return 0 end, function() return self.settings:readSetting("max") end),
|
||||
self:genSpinMenuItem(_("Max input idle seconds"), "max", function() return 0 end),
|
||||
self:genSpinMenuItem(_("Input window seconds"), "win", function() return 0 end, function() return self.settings:readSetting("max") end),
|
||||
self:genSpinMenuItem(_("Always standby if battery below"), "bat", function() return 0 end, function() return 100 end),
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
-- We've received touch/key event, so delay stadby accordingly
|
||||
function AutoStandby:onInputEvent()
|
||||
local config = self.settings.data
|
||||
local t = os.time()
|
||||
if t < self.lastInput + config.filter then
|
||||
-- packed too close together, ignore
|
||||
logger.dbg("AutoStandby: input packed too close to previous one, ignoring")
|
||||
return
|
||||
end
|
||||
|
||||
-- Nuke past timer as we'll reschedule the allow (or not)
|
||||
UIManager:unschedule(self.allow)
|
||||
|
||||
if PowerD:getCapacityHW() <= config.bat then
|
||||
-- battery is below threshold, so allow standby aggressively
|
||||
logger.dbg("AutoStandby: battery below threshold, enabling aggressive standby")
|
||||
self:allow()
|
||||
return
|
||||
elseif t > self.lastInput + config.max then
|
||||
-- too far apart, so reset delay
|
||||
logger.dbg("AutoStandby: input too far in future, resetting adaptive standby delay from", self.delay, "to", config.min)
|
||||
self.delay = config.min
|
||||
elseif t < self.lastInput + self.delay + config.win then
|
||||
-- otherwise widen the delay - "adaptive" - with frequent inputs, but don't grow beyonnd the max
|
||||
self.delay = math.min((self.delay+1) * config.mul, config.max)
|
||||
logger.dbg("AutoStandby: increasing standby delay to", self.delay)
|
||||
end -- equilibrium: when the event arrives beyond delay + win, but still below max, we keep the delay as-is
|
||||
|
||||
self.lastInput = t
|
||||
|
||||
if not self:isAllowedByConfig() then
|
||||
-- all standbys forbidden, always prevent
|
||||
self:prevent()
|
||||
return
|
||||
elseif delay == 0 then
|
||||
-- If delay is 0 now, just allow straight
|
||||
self:allow()
|
||||
return
|
||||
end
|
||||
-- otherwise prevent for a while for duration of the delay
|
||||
self:prevent()
|
||||
-- and schedule standby re-enable once delay expires
|
||||
UIManager:scheduleIn(self.delay, self.allow, self)
|
||||
end
|
||||
|
||||
-- Prevent standby (by timer)
|
||||
function AutoStandby:prevent()
|
||||
if not self.preventing then
|
||||
self.preventing = true
|
||||
UIManager:preventStandby()
|
||||
end
|
||||
end
|
||||
|
||||
-- Allow standby (by timer)
|
||||
function AutoStandby:allow()
|
||||
if self.preventing then
|
||||
self.preventing = false
|
||||
UIManager:allowStandby()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoStandby:isAllowedByConfig()
|
||||
return self.settings:isFalse("forbidden")
|
||||
end
|
||||
|
||||
function AutoStandby:genSpinMenuItem(text, cfg, min, max)
|
||||
return {
|
||||
keep_menu_open = true,
|
||||
text = text,
|
||||
enabled_func = function() return self:isAllowedByConfig() end,
|
||||
callback = function()
|
||||
local spin = SpinWidget:new {
|
||||
width = math.floor(Device.screen:getWidth() * 0.6),
|
||||
value = self.settings:readSetting(cfg),
|
||||
value_min = min and min() or 0,
|
||||
value_max = max and max() or 9999,
|
||||
value_hold_step = 10,
|
||||
ok_text = "Update",
|
||||
title_text = text,
|
||||
callback = function(spin) self.settings:saveSetting(cfg, spin.value):flush() end,
|
||||
}
|
||||
UIManager:show(spin)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
-- koreader is merely waiting for user input right now.
|
||||
-- UI signals us that standby is allowed at this very moment because nothing else goes on in the background.
|
||||
function AutoStandby:onAllowStandby()
|
||||
logger.dbg("AutoStandby: onAllowStandby()")
|
||||
-- In case the OS frontend itself doesn't manage power state, we can do it on our own here.
|
||||
-- One should also configure wake-up pins and perhaps wake alarm,
|
||||
-- if we want to enter deeper sleep states later on from within standby.
|
||||
|
||||
--os.execute("echo mem > /sys/power/state")
|
||||
end
|
||||
|
||||
return AutoStandby
|
||||
|
Loading…
Reference in New Issue