|
|
|
@ -44,7 +44,7 @@ end
|
|
|
|
|
local AutoWarmth = WidgetContainer:new{
|
|
|
|
|
name = "autowarmth",
|
|
|
|
|
sched_times = {},
|
|
|
|
|
sched_funcs = {}, -- necessary for unschedule, function, warmth
|
|
|
|
|
sched_warmths = {}, -- necessary for unschedule, function, warmth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-- get timezone offset in hours (including dst)
|
|
|
|
@ -117,7 +117,7 @@ function AutoWarmth:onAutoWarmthMode()
|
|
|
|
|
self:scheduleMidnightUpdate()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AutoWarmth:onResume()
|
|
|
|
|
function AutoWarmth:leavePowerSavingState(from_resume)
|
|
|
|
|
if self.activate == 0 then return end
|
|
|
|
|
|
|
|
|
|
logger.dbg("AutoWarmth: onResume/onLeaveStandby")
|
|
|
|
@ -127,42 +127,45 @@ function AutoWarmth:onResume()
|
|
|
|
|
if resume_date.day == SunTime.date.day and resume_date.month == SunTime.date.month
|
|
|
|
|
and resume_date.year == SunTime.date.year then
|
|
|
|
|
local now = SunTime:getTimeInSec(resume_date)
|
|
|
|
|
self:scheduleWarmthChanges(now)
|
|
|
|
|
self:scheduleNextWarmthChange(now, self.sched_warmth_index, from_resume)
|
|
|
|
|
-- reschedule 5sec after midnight
|
|
|
|
|
UIManager:scheduleIn(24*3600 + 5 - now, self.scheduleMidnightUpdate, self)
|
|
|
|
|
else
|
|
|
|
|
self:scheduleMidnightUpdate() -- resume is on the other day, do all calcs again
|
|
|
|
|
self:scheduleMidnightUpdate(from_resume) -- resume is on the other day, do all calcs again
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
AutoWarmth.onLeaveStandby = AutoWarmth.onResume
|
|
|
|
|
function AutoWarmth:onResume()
|
|
|
|
|
self:leavePowerSavingState(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AutoWarmth:onLeaveStandby()
|
|
|
|
|
self:leavePowerSavingState(false)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- wrapper for unscheduling, so that only our setWarmth gets unscheduled
|
|
|
|
|
function AutoWarmth.setWarmth(val, force)
|
|
|
|
|
if val then
|
|
|
|
|
if val > 100 then
|
|
|
|
|
DeviceListener:onSetNightMode(true)
|
|
|
|
|
else
|
|
|
|
|
DeviceListener:onSetNightMode(false)
|
|
|
|
|
end
|
|
|
|
|
if Device:hasNaturalLight() then
|
|
|
|
|
val = math.min(val, 100)
|
|
|
|
|
Device.powerd:setWarmth(val, force)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
function AutoWarmth:onSuspend()
|
|
|
|
|
if self.activate == 0 then return end
|
|
|
|
|
UIManager:unschedule(self.scheduleMidnightUpdate)
|
|
|
|
|
UIManager:unschedule(self.setWarmth)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AutoWarmth:scheduleMidnightUpdate()
|
|
|
|
|
AutoWarmth.onEnterStandby = AutoWarmth.onSuspend
|
|
|
|
|
|
|
|
|
|
-- from_resume ... true if called from onResume
|
|
|
|
|
function AutoWarmth:scheduleMidnightUpdate(from_resume)
|
|
|
|
|
logger.dbg("AutoWarmth: scheduleMidnightUpdate")
|
|
|
|
|
-- first unschedule all old functions
|
|
|
|
|
UIManager:unschedule(self.scheduleMidnightUpdate) -- when called from menu or resume
|
|
|
|
|
UIManager:unschedule(self.scheduleMidnightUpdate)
|
|
|
|
|
UIManager:unschedule(AutoWarmth.setWarmth)
|
|
|
|
|
|
|
|
|
|
SunTime:setPosition(self.location, self.latitude, self.longitude,
|
|
|
|
|
self.timezone, self.altitude, true)
|
|
|
|
|
SunTime:setPosition(self.location, self.latitude, self.longitude, self.timezone, self.altitude, true)
|
|
|
|
|
SunTime:setAdvanced()
|
|
|
|
|
SunTime:setDate() -- today
|
|
|
|
|
SunTime:calculateTimes()
|
|
|
|
|
|
|
|
|
|
self.sched_times = {}
|
|
|
|
|
self.sched_funcs = {}
|
|
|
|
|
self.sched_warmths = {}
|
|
|
|
|
|
|
|
|
|
local function prepareSchedule(times, index1, index2)
|
|
|
|
|
local time1 = times[index1]
|
|
|
|
@ -170,7 +173,7 @@ function AutoWarmth:scheduleMidnightUpdate()
|
|
|
|
|
|
|
|
|
|
local time = SunTime:getTimeInSec(time1)
|
|
|
|
|
table.insert(self.sched_times, time)
|
|
|
|
|
table.insert(self.sched_funcs, {AutoWarmth.setWarmth, self.warmth[index1]})
|
|
|
|
|
table.insert(self.sched_warmths, self.warmth[index1])
|
|
|
|
|
|
|
|
|
|
local time2 = times[index2]
|
|
|
|
|
if not time2 then return end -- to near to the pole
|
|
|
|
@ -185,8 +188,7 @@ function AutoWarmth:scheduleMidnightUpdate()
|
|
|
|
|
-- which map to warmth 0, 10, 20, 30 ... 100)
|
|
|
|
|
if frac(next_warmth * device_warmth_fit_scale) == 0 then
|
|
|
|
|
table.insert(self.sched_times, time + delta_t * i)
|
|
|
|
|
table.insert(self.sched_funcs, {self.setWarmth,
|
|
|
|
|
math.floor(math.min(self.warmth[index1], 100) + delta_w * i)})
|
|
|
|
|
table.insert(self.sched_warmths, math.floor(math.min(self.warmth[index1], 100) + delta_w * i))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
@ -249,58 +251,80 @@ function AutoWarmth:scheduleMidnightUpdate()
|
|
|
|
|
local now = SunTime:getTimeInSec()
|
|
|
|
|
|
|
|
|
|
-- reschedule 5sec after midnight
|
|
|
|
|
UIManager:scheduleIn(24*3600 + 5 - now, self.scheduleMidnightUpdate, self )
|
|
|
|
|
|
|
|
|
|
self:scheduleWarmthChanges(now)
|
|
|
|
|
UIManager:scheduleIn(24*3600 + 5 - now, self.scheduleMidnightUpdate, self)
|
|
|
|
|
-- and schedule the first warmth change
|
|
|
|
|
self:scheduleNextWarmthChange(now, 1, from_resume)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- @todo: As we have standby now, don't do the scheduling of the whole schedule,
|
|
|
|
|
-- but only the next warmth value plus an additional scheduleWarmthChanges
|
|
|
|
|
-- This would safe a bit of energy, but not really much.
|
|
|
|
|
function AutoWarmth:scheduleWarmthChanges(time)
|
|
|
|
|
logger.dbg("AutoWarmth: scheduleWarmthChanges")
|
|
|
|
|
for i = 1, #self.sched_funcs do -- loop not essential, as unschedule unschedules all functions at once
|
|
|
|
|
if not UIManager:unschedule(self.sched_funcs[i][1]) then
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
-- schedules the next warmth change
|
|
|
|
|
-- search_pos ... start searching from that index
|
|
|
|
|
-- from_resume ... true if first call after resume
|
|
|
|
|
function AutoWarmth:scheduleNextWarmthChange(time, search_pos, from_resume)
|
|
|
|
|
logger.dbg("AutoWarmth: scheduleWarmthChange")
|
|
|
|
|
if UIManager:unschedule(AutoWarmth.setWarmth) then
|
|
|
|
|
logger.err("AutoWarmth: abnormal unschedule, time changed?")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
UIManager:unschedule(AutoWarmth.setWarmth) -- to be safe, if there are no scheduled entries
|
|
|
|
|
if self.activate == 0 or #self.sched_warmths == 0 or search_pos > #self.sched_warmths then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if self.activate == 0 then return end
|
|
|
|
|
if #self.sched_funcs == 0 then return end
|
|
|
|
|
self.sched_warmth_index = search_pos or 1
|
|
|
|
|
|
|
|
|
|
-- `actual_warmth` is the value which should be applied now.
|
|
|
|
|
-- `next_warmth` is valid `delay_time` seconds after now for resume on some devices (KA1)
|
|
|
|
|
-- `next_warmth` is valid `delay_s` seconds after now for resume on some devices (KA1)
|
|
|
|
|
-- Most of the times this will be the same as `actual_warmth`.
|
|
|
|
|
-- We need both, as we could have a very rapid change in warmth (depending on user settings)
|
|
|
|
|
-- or by chance a change in warmth very shortly after (a few ms) resume time.
|
|
|
|
|
local delay_time = 1.5
|
|
|
|
|
local delay_s = 1.5
|
|
|
|
|
-- Use the last warmth value, so that we have a valid value when resuming after 24:00 but
|
|
|
|
|
-- before true midnight. OK, this value is actually not quite the right one, as it is calculated
|
|
|
|
|
-- for the current day (and not the previous one), but this is for a corner case
|
|
|
|
|
-- and the error is small.
|
|
|
|
|
local actual_warmth = self.sched_funcs[#self.sched_funcs][2]
|
|
|
|
|
local actual_warmth = self.sched_warmths[self.sched_warmth_index or #self.sched_warmths]
|
|
|
|
|
local next_warmth = actual_warmth
|
|
|
|
|
for i = 1, #self.sched_funcs do
|
|
|
|
|
for i = self.sched_warmth_index, #self.sched_warmths do
|
|
|
|
|
if self.sched_times[i] <= time then
|
|
|
|
|
actual_warmth = self.sched_funcs[i][2] or actual_warmth
|
|
|
|
|
actual_warmth = self.sched_warmths[i] or actual_warmth
|
|
|
|
|
if self.sched_times[i] <= time + delay_s then
|
|
|
|
|
next_warmth = self.sched_warmths[i] or next_warmth
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
UIManager:scheduleIn(self.sched_times[i] - time,
|
|
|
|
|
self.sched_funcs[i][1], self.sched_funcs[i][2])
|
|
|
|
|
end
|
|
|
|
|
if self.sched_times[i] <= time + delay_time then
|
|
|
|
|
next_warmth = self.sched_funcs[i][2] or next_warmth
|
|
|
|
|
self.sched_warmth_index = i
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
-- update current warmth immediately
|
|
|
|
|
self.setWarmth(actual_warmth)
|
|
|
|
|
self:setWarmth(actual_warmth, false) -- no setWarmth rescheduling, don't force warmth
|
|
|
|
|
local next_sched_time = self.sched_times[self.sched_warmth_index] - time
|
|
|
|
|
if self.sched_warmth_index <= #self.sched_warmths and next_sched_time > 0 then
|
|
|
|
|
-- This setWarmth will call scheduleNextWarmthChange which will schedule setWarmth again.
|
|
|
|
|
UIManager:scheduleIn(next_sched_time, self.setWarmth, self, self.sched_warmths[self.sched_warmth_index], true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if from_resume then
|
|
|
|
|
-- On some strange devices like KA1 setWarmth doesn't work right after a resume so
|
|
|
|
|
-- schedule setting of another valid warmth (=`next_warmth`) again (one time).
|
|
|
|
|
-- On sane devices this schedule does no harm.
|
|
|
|
|
-- see https://github.com/koreader/koreader/issues/8363
|
|
|
|
|
UIManager:scheduleIn(delay_s, self.setWarmth, self, next_warmth, true) -- no setWarmth rescheduling, force warmth
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- On some strange devices like KA1 the above doesn't work right after a resume so
|
|
|
|
|
-- schedule setting of another valid warmth (=`next_warmth`) again (one time).
|
|
|
|
|
-- On sane devices this schedule does no harm.
|
|
|
|
|
-- see https://github.com/koreader/koreader/issues/8363
|
|
|
|
|
UIManager:scheduleIn(delay_time, self.setWarmth, next_warmth, true)
|
|
|
|
|
-- Set warmth and schedule the next warmth change
|
|
|
|
|
function AutoWarmth:setWarmth(val, schedule_next, force_warmth)
|
|
|
|
|
if val then
|
|
|
|
|
DeviceListener:onSetNightMode(val > 100)
|
|
|
|
|
|
|
|
|
|
if Device:hasNaturalLight() then
|
|
|
|
|
val = math.min(val, 100) -- "mask" night mode
|
|
|
|
|
Device.powerd:setWarmth(val, force_warmth)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if schedule_next then
|
|
|
|
|
local now = SunTime:getTimeInSec()
|
|
|
|
|
self:scheduleNextWarmthChange(now, self.sched_warmth_index, false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function AutoWarmth:hoursToClock(hours)
|
|
|
|
@ -312,8 +336,7 @@ end
|
|
|
|
|
|
|
|
|
|
function AutoWarmth:addToMainMenu(menu_items)
|
|
|
|
|
menu_items.autowarmth = {
|
|
|
|
|
text = Device:hasNaturalLight() and _("Auto warmth and night mode")
|
|
|
|
|
or _("Auto night mode"),
|
|
|
|
|
text = Device:hasNaturalLight() and _("Auto warmth and night mode") or _("Auto night mode"),
|
|
|
|
|
checked_func = function() return self.activate ~= 0 end,
|
|
|
|
|
sub_item_table_func = function()
|
|
|
|
|
return self:getSubMenuItems()
|
|
|
|
@ -349,8 +372,7 @@ To use the sun's position, a geographical location must be entered. The calculat
|
|
|
|
|
function AutoWarmth:getSubMenuItems()
|
|
|
|
|
return {
|
|
|
|
|
{
|
|
|
|
|
text = Device:hasNaturalLight() and _("About auto warmth and night mode")
|
|
|
|
|
or _("About auto night mode"),
|
|
|
|
|
text = Device:hasNaturalLight() and _("About auto warmth and night mode") or _("About auto night mode"),
|
|
|
|
|
callback = function()
|
|
|
|
|
UIManager:show(InfoMessage:new{
|
|
|
|
|
text = about_text,
|
|
|
|
@ -397,8 +419,7 @@ function AutoWarmth:getSubMenuItems()
|
|
|
|
|
enabled_func = function()
|
|
|
|
|
return self.activate ~=0
|
|
|
|
|
end,
|
|
|
|
|
text = Device:hasNaturalLight() and _("Warmth and night mode settings")
|
|
|
|
|
or _("Night mode settings"),
|
|
|
|
|
text = Device:hasNaturalLight() and _("Warmth and night mode settings") or _("Night mode settings"),
|
|
|
|
|
sub_item_table = self:getWarmthMenu(),
|
|
|
|
|
separator = true,
|
|
|
|
|
},
|
|
|
|
@ -415,11 +436,7 @@ function AutoWarmth:getActivateMenu()
|
|
|
|
|
help_text = help_text,
|
|
|
|
|
checked_func = function() return self.activate == activator end,
|
|
|
|
|
callback = function()
|
|
|
|
|
if self.activate ~= activator then
|
|
|
|
|
self.activate = activator
|
|
|
|
|
else
|
|
|
|
|
self.activate = 0
|
|
|
|
|
end
|
|
|
|
|
self.activate = self.activate ~= activator and activator or 0
|
|
|
|
|
G_reader_settings:saveSetting("autowarmth_activate", self.activate)
|
|
|
|
|
self:scheduleMidnightUpdate()
|
|
|
|
|
end,
|
|
|
|
@ -654,7 +671,7 @@ function AutoWarmth:getScheduleMenu()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local retval = {
|
|
|
|
|
getScheduleMenuEntry(_("Solar midnight"), 1, false ),
|
|
|
|
|
getScheduleMenuEntry(_("Solar midnight"), 1, false),
|
|
|
|
|
getScheduleMenuEntry(_("Astronomical dawn"), 2, false),
|
|
|
|
|
getScheduleMenuEntry(_("Nautical dawn"), 3, false),
|
|
|
|
|
getScheduleMenuEntry(_("Civil dawn"), 4),
|
|
|
|
@ -754,8 +771,7 @@ function AutoWarmth:getWarmthMenu()
|
|
|
|
|
|
|
|
|
|
local retval = {
|
|
|
|
|
{
|
|
|
|
|
text = Device:hasNaturalLight() and _("Set warmth and night mode for:")
|
|
|
|
|
or _("Set night mode for:"),
|
|
|
|
|
text = Device:hasNaturalLight() and _("Set warmth and night mode for:") or _("Set night mode for:"),
|
|
|
|
|
enabled_func = function() return false end,
|
|
|
|
|
},
|
|
|
|
|
getWarmthMenuEntry(_("Solar noon"), 6, false),
|
|
|
|
@ -910,7 +926,7 @@ function AutoWarmth:getLocationString()
|
|
|
|
|
if self.location ~= "" then
|
|
|
|
|
return self.location
|
|
|
|
|
else
|
|
|
|
|
return "(" .. self.latitude .. "," .. self.longitude .. ")"
|
|
|
|
|
return string.format("(%.2f°,%.2f°)", self.latitude, self.longitude)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|