Fix: resolve sunrise/sunset times in desktop scheduler
Sun-based rules (startTime -2=sunrise, -3=sunset) were silently skipped with 'if (startSecs < 0) continue'. The sun.js calculator existed but was never called. - Import calcSunTimes from ./core/sun - Compute today's sunrise/sunset once at start of _loadSchedule() - resolveSecs() maps -2/-3 sentinels to actual seconds + offset - Both Away Mode and Schedule sections now fire at correct sun times - Rules with no location set continue to be skipped gracefully Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
const wemo = require('./wemo');
|
const wemo = require('./wemo');
|
||||||
const store = require('./store');
|
const store = require('./store');
|
||||||
|
const { sunTimes: calcSunTimes } = require('./core/sun');
|
||||||
|
|
||||||
// ── Helpers ──────────────────────────────────────────────────────────────────
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -126,6 +127,29 @@ class LocalScheduler {
|
|||||||
const schedule = [];
|
const schedule = [];
|
||||||
const rules = store.getDwmRules();
|
const rules = store.getDwmRules();
|
||||||
|
|
||||||
|
// Compute today's sunrise/sunset once — used to resolve sun-based start/end times
|
||||||
|
const loc = store.getLocation();
|
||||||
|
const todaySun = (loc?.lat != null && loc?.lng != null)
|
||||||
|
? calcSunTimes(loc.lat, loc.lng)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a raw stored time value to seconds-from-midnight.
|
||||||
|
* -2 = sunrise sentinel, -3 = sunset sentinel (set by RuleEditor.jsx saveDwm).
|
||||||
|
* offsetMins is added/subtracted from the sun time (e.g. "30 min before sunset").
|
||||||
|
* Returns null if the time cannot be resolved (no location, polar day/night, or no time set).
|
||||||
|
*/
|
||||||
|
const resolveSecs = (rawSecs, type, offsetMins) => {
|
||||||
|
const offsetSecs = (offsetMins ?? 0) * 60;
|
||||||
|
if (type === 'sunset' || rawSecs === -3) {
|
||||||
|
return todaySun?.sunset != null ? todaySun.sunset + offsetSecs : null;
|
||||||
|
}
|
||||||
|
if (type === 'sunrise' || rawSecs === -2) {
|
||||||
|
return todaySun?.sunrise != null ? todaySun.sunrise + offsetSecs : null;
|
||||||
|
}
|
||||||
|
return rawSecs >= 0 ? rawSecs : null;
|
||||||
|
};
|
||||||
|
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
if (!rule.enabled) continue;
|
if (!rule.enabled) continue;
|
||||||
|
|
||||||
@@ -134,9 +158,9 @@ class LocalScheduler {
|
|||||||
|
|
||||||
// ── Away Mode — handled by the randomisation loop, not pre-computed entries ──
|
// ── Away Mode — handled by the randomisation loop, not pre-computed entries ──
|
||||||
if (rule.type === 'Away') {
|
if (rule.type === 'Away') {
|
||||||
const startSecs = Number(rule.startTime ?? -1);
|
const startSecs = resolveSecs(Number(rule.startTime ?? -1), rule.startType, rule.startOffset);
|
||||||
const endSecs = Number(rule.endTime ?? -1);
|
const endSecs = resolveSecs(Number(rule.endTime ?? -1), rule.endType, rule.endOffset);
|
||||||
if (startSecs < 0) continue; // sun-based — skip for now
|
if (startSecs === null) continue; // no location set or polar day/night
|
||||||
|
|
||||||
for (const dayId of (rule.days ?? [])) {
|
for (const dayId of (rule.days ?? [])) {
|
||||||
const td0 = rule.targetDevices?.[0]; // for status display only
|
const td0 = rule.targetDevices?.[0]; // for status display only
|
||||||
@@ -152,7 +176,7 @@ class LocalScheduler {
|
|||||||
isAwayStart: true,
|
isAwayStart: true,
|
||||||
});
|
});
|
||||||
// Window-end entry: stops the away loop
|
// Window-end entry: stops the away loop
|
||||||
if (endSecs >= 0) {
|
if (endSecs !== null && endSecs >= 0) {
|
||||||
schedule.push({
|
schedule.push({
|
||||||
ruleId: rule.id + '-away-end',
|
ruleId: rule.id + '-away-end',
|
||||||
ruleName: rule.name,
|
ruleName: rule.name,
|
||||||
@@ -196,11 +220,11 @@ class LocalScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Schedule / other time-based rules ────────────────────────────────
|
// ── Schedule / other time-based rules ────────────────────────────────
|
||||||
const startSecs = Number(rule.startTime ?? -1);
|
const startSecs = resolveSecs(Number(rule.startTime ?? -1), rule.startType, rule.startOffset);
|
||||||
const endSecs = Number(rule.endTime ?? -1);
|
const endSecs = resolveSecs(Number(rule.endTime ?? -1), rule.endType, rule.endOffset);
|
||||||
const startAction = Number(rule.startAction ?? 1);
|
const startAction = Number(rule.startAction ?? 1);
|
||||||
const endAction = Number(rule.endAction ?? -1);
|
const endAction = Number(rule.endAction ?? -1);
|
||||||
if (startSecs < 0) continue;
|
if (startSecs === null) continue; // no location set, polar day/night, or no time defined
|
||||||
|
|
||||||
for (const dayId of (rule.days ?? [])) {
|
for (const dayId of (rule.days ?? [])) {
|
||||||
for (const td of (rule.targetDevices ?? [])) {
|
for (const td of (rule.targetDevices ?? [])) {
|
||||||
@@ -210,7 +234,7 @@ class LocalScheduler {
|
|||||||
targetHost: td.host, targetPort: td.port,
|
targetHost: td.host, targetPort: td.port,
|
||||||
dayId: Number(dayId), targetSecs: startSecs, action: startAction });
|
dayId: Number(dayId), targetSecs: startSecs, action: startAction });
|
||||||
}
|
}
|
||||||
if (endSecs > 0 && endAction >= 0) {
|
if (endSecs !== null && endSecs > 0 && endAction >= 0) {
|
||||||
schedule.push({ ruleId: rule.id, ruleName: rule.name,
|
schedule.push({ ruleId: rule.id, ruleName: rule.name,
|
||||||
targetHost: td.host, targetPort: td.port,
|
targetHost: td.host, targetPort: td.port,
|
||||||
dayId: Number(dayId), targetSecs: endSecs, action: endAction });
|
dayId: Number(dayId), targetSecs: endSecs, action: endAction });
|
||||||
|
|||||||
Reference in New Issue
Block a user