<commit_before>const CHECK_INTERVAL = 30000;

export default class Scheduler {
    constructor(storage,history,logger,valveController) {
        this.storage = storage;
        this.history = history;
        this.logger = logger;
        this.valveController = valveController;

        this.schedule = [];
        this.waterUntil = 0;
    }
    start() {
        this.check();
    }
    reload() {
        this.logger.info('Restarting scheduler...');
        this.storage.getItem('schedule',(err,value) => {
           if (err) {
                return this.logger.error(`Unable to reload schedule ${err.stack}`);
           }
           if (!value) {
                this.schedule = [];
                this.waterUntil = 0;
           } else {
                this.waterUntil = value.waterUntil;
                this.schedule = value.items;
           }
        });
    }
    check = () => {
        const now = new Date();
        const currentHour = now.getHours(); 
        this.logger.verbose(`Checking schedule for hour ${currentHour}`);
        let updated = false;
        this.schedule.map(item => {
            this.logger.debug(JSON.stringify(item));
            if (item.time === currentHour 
            && (!item.nextRun || now.getTime() >= item.nextRun)
            // don't start new tasks if we're more than 5 minutes
            // past the hour
            && now.getMinutes() < 5) {
                item.nextRun = this.startOfDay(now) + ((86400 * item.frequency) + (3600 * item.time)) * 1000;
                this.logger.verbose(`Schedule item will run - next run ${item.nextRun}`);
                const nextWater = now.getTime() + (item.duration * 60 * 1000);
                if (nextWater > this.waterUntil) {
                    this.logger.verbose(`Will water until ${nextWater}`);
                    this.waterUntil = nextWater;
                    updated = true;
                }
            }
        });

        const checkValve = () => {
            this.valveController.setOpen(this.waterUntil > now.getTime(),'scheduler',err => {
               if (err) {
                    this.logger.error(`Unable to set valve status ${err.stack}`);
                }
                setTimeout(this.check,CHECK_INTERVAL);
            });
        }

        if (updated) {
            this.storage.setItem('schedule',{ 
                waterUntil: this.waterUntil,
                items: this.schedule 
            },err => {
                if (err) {
                    return this.logger.error(`Unable to update schedule ${err.stack}`);
                }
                checkValve();
            });
        } else {
            checkValve();
        }
    }
    startOfDay(now) {
        return (new Date(now.getFullYear(),now.getMonth(),now.getDate())).getTime();
    }

}<commit_msg>Scheduler no longer stops watering set manually by the user<commit_after>const CHECK_INTERVAL = 30000;

export default class Scheduler {
    constructor(storage,history,logger,valveController) {
        this.storage = storage;
        this.history = history;
        this.logger = logger;
        this.valveController = valveController;

        this.schedule = [];
        this.waterUntil = 0;
        this.valveController.on('setOpen',function({source}) {
            // manually setting the valve disables any
            // scheduled waterings
            if (source !== 'scheduler') {
                this.waterUntil = 0;
            }
        });
    }
    start() {
        this.check();
    }
    reload() {
        this.logger.info('Restarting scheduler...');
        this.storage.getItem('schedule',(err,value) => {
           if (err) {
                return this.logger.error(`Unable to reload schedule ${err.stack}`);
           }
           if (!value) {
                this.schedule = [];
           } else {
                this.schedule = value.items;
           }
        });
    }
    check = () => {
        const now = new Date();
        const currentHour = now.getHours(); 
        this.logger.verbose(`Checking schedule for hour ${currentHour}`);
        let updated = false;
        this.schedule.map(item => {
            this.logger.debug(JSON.stringify(item));
            if (item.time === currentHour 
            && (!item.nextRun || now.getTime() >= item.nextRun)
            // don't start new tasks if we're more than 5 minutes
            // past the hour
            && now.getMinutes() < 5) {
                item.nextRun = this.startOfDay(now) + ((86400 * item.frequency) + (3600 * item.time)) * 1000;
                this.logger.verbose(`Schedule item will run - next run ${item.nextRun}`);
                const nextWater = now.getTime() + (item.duration * 60 * 1000);
                if (nextWater > this.waterUntil) {
                    this.logger.verbose(`Will water until ${nextWater}`);
                    this.waterUntil = nextWater;
                    updated = true;
                }
            }
        });

        const checkValve = () => {
            if (!this.waterUntil) return;
            this.valveController.setOpen(this.waterUntil > now.getTime(),'scheduler',err => {
               if (err) {
                    this.logger.error(`Unable to set valve status ${err.stack}`);
                }
                setTimeout(this.check,CHECK_INTERVAL);
            });
        }

        if (updated) {
            this.storage.setItem('schedule',{ 
                items: this.schedule 
            },err => {
                if (err) {
                    return this.logger.error(`Unable to update schedule ${err.stack}`);
                }
                checkValve();
            });
        } else {
            checkValve();
        }
    }
    startOfDay(now) {
        return (new Date(now.getFullYear(),now.getMonth(),now.getDate())).getTime();
    }

}
{'fraction_non_alphanumeric': 0.1294902497434143, 'fraction_numerical': 0.009579199452617175, 'mean_word_length': 1.497650576676634, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5408616', 'n_tokens_mistral': 1637, 'n_tokens_neox': 1536, 'n_words': 318}