telldus/telldus-gui/Plugins/Scheduler/__init__.js
Stefan Persson 9477869e6c Code cleanup
2011-02-16 10:58:30 +00:00

373 lines
No EOL
12 KiB
JavaScript

__setupPackage__( __extension__ );
__postInit__ = function() {
application.allDoneLoading.connect( com.telldus.scheduler.init );
}
com.telldus.scheduler = function() {
var storedJobs = new MappedList(); //all jobs, added by "addJob"
var joblist; //sorted list containing next run time (and id) for all storedJobs
var timerid = null; //id of currently running timer, used when timer should be aborted
var queuedJob; //job currently in running timer, is already removed from joblist
//TODO (perhaps) run something on the LAST day of the month?
//TODO remove absolute events after being run... Cannot do that from here, must be done from loading/saving plugin
//TODO Logic in future extended scenes/macros (not here, but somewhere in telldus center): Delays - job that then continues a scene from a certain step after the delay
//TODO Logic in future extended scenes/macros (not here, but somewhere in telldus center): Conditions, see if certain condition is fulfilled when the schedule should
// run, for example engine heater if the temperature is below 5 degrees, or turn on light if it is cloudy (if not cloudy, wait 20 minutes
// Not only for schedule, other input (for example motion detectors) can be conditioned in the same way (for example, house inhabitants are set as "Gone").
// Repeat, if condition is not fulfilled, try again in x seconds
//TODO Logic in future extended scenes/macros (not here, but somewhere in telldus center): "Stop scene", i. e. when device is turned on, automatically turn it off again after x seconds
function init(){
JobDaylightSavingReload.prototype = new com.telldus.scheduler.Job();
setDaylightSavingJobFunctions();
}
function addJob(job){
if(storedJobs.length == 0){
print("Adding daylight saving time");
var daylightSavingReloadKey = storedJobs.push(getDaylightSavingReloadJob());
updateJobInList(daylightSavingReloadKey);
}
var key = storedJobs.push(job);
job.key = key;
updateJobInList(key);
return key;
}
//add several jobs at once, withou recalculating timer between each
function addJobs(jobs){
var returnKeys = new Array();
if(jobs == undefined || jobs.length == 0){
return returnKeys;
}
if(storedJobs.length == 0){
print("Adding daylight saving time");
var daylightSavingReloadKey = storedJobs.push(getDaylightSavingReloadJob());
updateJobInList(daylightSavingReloadKey, true); //waitForMore = don't sort array and recalculate timer yet
}
for(var i=0;i<jobs.length;i++){
var job = jobs[i];
var key = storedJobs.push(job);
job.key = key;
updateJobInList(key, true); //waitForMore = don't sort array and recalculate timer yet
returnKeys.push(key);
}
joblist.sort(compareTime);
runNextJob();
return returnKeys;
}
function fuzzify(currentTimestamp, fuzzinessBefore, fuzzinessAfter, lastRun){
if(fuzzinessAfter != 0 || fuzzinessBefore != 0){
var now = new Date().getTime();
var fuzzinessBeforeOrig = fuzzinessBefore*1000;
if(currentTimestamp - (fuzzinessBefore*1000) < now){
//print("Already in interval");
fuzzinessBefore = (currentTimestamp - now)/1000; //we have already entered the fuzzy-interval, move the start point to "now"...
}
//print("LASTRUN: " + lastRun + " - " + new Date(lastRun));
//print("COMPARE: " + (currentTimestamp - (fuzzinessBefore*1000)) + " - " + new Date(currentTimestamp - (fuzzinessBefore*1000)));
//print("COMPARE: " + (currentTimestamp - fuzzinessBeforeOrig) + " - " + new Date(currentTimestamp - fuzzinessBeforeOrig));
if(lastRun != undefined && lastRun >= (currentTimestamp - fuzzinessBeforeOrig)){
//print("JUST RETURNING");
return 0; //job already run in this interval, don't run it again...
}
//print("CONTINUING HAPPILY");
var interval = fuzzinessAfter + fuzzinessBefore;
var rand = Math.random(); //Random enough at the moment
var fuzziness = Math.floor((interval+1) * rand);
fuzziness = fuzziness - fuzzinessBefore;
currentTimestamp += (fuzziness * 1000);
}
return currentTimestamp;
}
function getDaylightSavingReloadJob(){
return new JobDaylightSavingReload();
}
function recalculateAllJobs(){
print("Recalculating all jobs");
joblist = new Array();
for(var key in storedJobs.container){
var job = storedJobs.get(key);
var nextRunTime = job.getNextRunTime();
print("Run time: " + new Date(nextRunTime));
if(nextRunTime === null){
print("Will not run");
continue;
}
joblist.push(new RunJob(key, nextRunTime));
}
joblist.sort(compareTime);
runNextJob();
}
function removeFromJobList(id){
if(!joblist){
return;
}
for(i=0;i<joblist.length;i++){
if(id==joblist[i].id){
joblist.splice(i, 1);
return;
}
}
}
function removeJob(id, waitForMore){
if(queuedJob.id == id){
queuedJob = undefined;
}
storedJobs.remove(id);
updateJobInList(id, waitForMore); //wait for more
if(storedJobs.length == 1){
//only one job left, it's only the DaylightSaving reload job, remove that too
for(var key in storedJobs.container){
storedJobs.remove(key);
updateJobInList(key);
}
}
}
function runJob(id){
print("Running job, will execute");
queuedJob = null;
var success = storedJobs.get(id).execute();
print("Job run, after delay " + id);
updateJobInList(id);
}
function runNextJob(){
if(timerid != null){
clearTimeout(timerid);
print("Timer interrupted");
}
if(joblist.length <= 0){
print("No jobs");
return; //no jobs, abort
}
if(queuedJob){
//put the currently queued job back in the list, so that it can be compared again
print("Queued job is something, put it back in list");
joblist.push(queuedJob);
joblist.sort(compareTime);
}
var job = joblist.shift(); //get first job in list (and remove it from the list)
queuedJob = job; //put it in list, to keep track of current job
var nextRunTime = job.nextRunTime;
if(nextRunTime === null){
//something is wrong
print("Something is wrong");
updateJobInList(job.id); //This will just recalculate the job, and probably return 0 again, but updateJobInList won't add it to the list in that case (shouldnt end up here at all now actually)
return;
}
var runJobFunc = function(){ runJob(job.id); };
var now = new Date().getTime();
var delay = nextRunTime - now;
print("Will run " + storedJobs.get(job.id).v.name + " (" + job.id + ") at " + new Date(nextRunTime)); //Note not all will have a name
print("(Now is " + new Date() + ")");
timerid = setTimeout(runJobFunc, delay); //start the timer
print("Has started a job wait");
}
function updateJobInList(id, waitForMore){
if(!joblist){
joblist = new Array();
}
if(!storedJobs.contains(id)){
removeFromJobList(id);
runNextJob();
return;
}
var job = storedJobs.get(id);
var nextRunTime = job.getNextRunTime();
print("Time updated to: " + new Date(nextRunTime));
if(nextRunTime === null){
print("Will not run this one");
removeFromJobList(id); //remove from joblist if it exists there (run time may have been updated to something invalid/already passed)
runNextJob(); //resort list (may have changed), run next
return;
}
joblist.push(new RunJob(id, nextRunTime));
if(waitForMore == undefined){
joblist.sort(compareTime);
runNextJob();
}
}
function updateJob(key, job){
if(!storedJobs.contains(key)){
return;
}
storedJobs.update(key, job);
updateJobInList(key);
}
function updateLastRun(id, lastRun){
print("Update last run: " + id + " to " + lastRun);
storedJobs.get(id).v.lastRun = lastRun; //update current list
}
function JobDaylightSavingReload(){
this.v.name = "Daylight Saving Time job";
}
function setDaylightSavingJobFunctions(){
JobDaylightSavingReload.prototype.execute = function(){
//override default
print("Daylight savings job");
//TODO Make sure this job is run "last", if other jobs are set to be run at the same second
setTimeout(recalculateAllJobs(), 1); //sleep for one ms, to avoid strange calculations
//this may lead to that things that should be executed exactly at 3.00 when
//moving time forward one hour won't run, but that should be the only case
return 0;
};
JobDaylightSavingReload.prototype.getNextRunTime = function(){
print("getNextRunTime DaylightSaving");
var dst = DstDetect();
if(dst[0] == ""){
//not using dst in this timezone, still add it to the lists to keep it consistent (will be added as 1/1 1970)
print("Not using timezone");;
return null;
}
var now = new Date().getTime();
var time = dst[0].getTime();
if(now > time){
//already passed
time = dst[1].getTime();
}
return time;
}
}
function MappedList() {
this.container = {};
this.length = 0;
}
MappedList.prototype.contains = function(key){
return !(this.container[key] === undefined);
}
MappedList.prototype.get = function(key){
return this.container[key];
}
MappedList.prototype.push = function(element){
//TODO reusing keys at the moment, that's ok, right?
var length = this.length;
this.container[length] = element;
this.length = length + 1;
return length;
}
MappedList.prototype.remove = function(key){
delete this.container[key];
this.length--;
}
MappedList.prototype.update = function(key, element){
this.container[key] = element;
}
function RunJob(id, nextRunTime){ //, type, device, method, value){
this.id = id;
this.nextRunTime = nextRunTime;
}
return { //Public functions
addJob: addJob, //job, returns: storage id
addJobs: addJobs, //jobs (array), returns: storage id's in array
fuzzify: fuzzify, //timestamp, max fuzziness before, max fuzziness after, returns: new random timestamp within min/max fuzziness-boundries
removeJob: removeJob, //storage id
updateJob: updateJob, //storage id, job
updateLastRun: updateLastRun, //id, datetimestamp
init:init
}
}();
function compareTime(a, b) {
return a.nextRunTime - b.nextRunTime;
}
com.telldus.scheduler.Job = function(jobdata) {
if(jobdata){
this.v = jobdata;
}
else{
this.v = {};
}
}
com.telldus.scheduler.Job.prototype.execute = function(){
//may be overridden if other than device manipulation should be performed
var success = 0;
if(this.v.executeFunc != null){
success = this.v.executeFunc(this);
this.updateJobLastRun();
return success;
}
success = this.executeDefault();
//if(success){
//update last run even if not successful, else it may become an infinite loop (if using pastGracePeriod)
this.updateJobLastRun();
//}
return success;
};
com.telldus.scheduler.Job.prototype.executeDefault = function(){
var success = 0;
deviceid = this.v.device;
var method = parseInt(this.v.method);
//print("Job id: " + this.v.device + " Method: " + method);
switch(method){
case com.telldus.core.TELLSTICK_TURNON:
success = com.telldus.core.turnOn(deviceid);
break;
case com.telldus.core.TELLSTICK_TURNOFF:
success = com.telldus.core.turnOff(deviceid);
break;
case com.telldus.core.TELLSTICK_DIM:
success = com.telldus.core.dim(deviceid, this.v.value);
break;
case com.telldus.core.TELLSTICK_BELL:
success = com.telldus.core.bell(deviceid);
break;
default:
break;
}
return success;
};
com.telldus.scheduler.Job.prototype.getNextRunTime = function(){
print("getNextRunTime default");
return null; //default
}
com.telldus.scheduler.Job.prototype.updateJobLastRun = function(){
var timestamp = new Date().getTime();
com.telldus.scheduler.updateLastRun(this.key, timestamp);
//override this to save last run to storage too, but don't forget to call the above method too
}
include("DefaultJobTypes.js");
include("DaylightSavingTime.js");