Skip to content

Commit

Permalink
Add core scheduler for all entities / integrations (GladysAssistant#1641
Browse files Browse the repository at this point in the history
)
  • Loading branch information
atrovato authored and euguuu committed Mar 5, 2023
1 parent fc4ea6d commit 6e65d6f
Show file tree
Hide file tree
Showing 45 changed files with 2,453 additions and 1,650 deletions.
8 changes: 4 additions & 4 deletions server/config/scheduler-jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ const { EVENTS } = require('../utils/constants');
const jobs = [
{
name: 'check-gladys-upgrade',
frequencyInSeconds: 6 * 60 * 60,
rule: '0 0 */6 * * *', // every 6 hours
event: EVENTS.SYSTEM.CHECK_UPGRADE,
},
{
name: 'purge-device-states',
frequencyInSeconds: 4 * 60 * 60,
rule: '0 30 */4 * * *', // every 4 hours
event: EVENTS.DEVICE.PURGE_STATES,
},
{
name: 'hourly-device-state-aggregate',
frequencyInSeconds: 60 * 60,
rule: '0 0 * * * *', // every hour
event: EVENTS.DEVICE.CALCULATE_HOURLY_AGGREGATE,
},
{
name: 'daily-purge-of-old-jobs',
frequencyInSeconds: 24 * 60 * 60,
rule: '0 0 22 * * *', // every day at 22:00
event: EVENTS.JOB.PURGE_OLD_JOBS,
},
];
Expand Down
4 changes: 2 additions & 2 deletions server/lib/gateway/gateway.handleNewMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ async function handleNewMessage(data, rawMessage, cb) {

// if the message is a Google Home request
if (data.type === 'gladys-open-api' && data.action === 'google-home-request') {
this.handleGoogleHomeMessage(data, rawMessage, cb);
await this.handleGoogleHomeMessage(data, rawMessage, cb);
}

// if the message is a Alexa request
if (data.type === 'gladys-open-api' && data.action === 'alexa-request') {
this.handleAlexaMessage(data, rawMessage, cb);
await this.handleAlexaMessage(data, rawMessage, cb);
}
}

Expand Down
9 changes: 2 additions & 7 deletions server/lib/gateway/gateway.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,8 @@ async function init() {
// schedule backup at midnight
const timezone = await this.variable.getValue(SYSTEM_VARIABLE_NAMES.TIMEZONE);

const rule = new this.schedule.RecurrenceRule();
rule.tz = timezone;
rule.hour = 0;
rule.minute = 0;
rule.second = 0;

this.backupSchedule = this.schedule.scheduleJob(rule, this.checkIfBackupNeeded.bind(this));
const rule = { tz: timezone, hour: 0, minute: 0, second: 0 };
this.backupSchedule = this.scheduler.scheduleJob(rule, this.checkIfBackupNeeded.bind(this));

// Get latest Gladys version in 5 minutes
// To let the system initialize
Expand Down
9 changes: 5 additions & 4 deletions server/lib/gateway/gateway.login.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
const get = require('get-value');
const { webcrypto } = require('crypto');
const getConfig = require('../../utils/getConfig');
const logger = require('../../utils/logger');
const { ERROR_MESSAGES } = require('../../utils/constants');
const { Error403, Error500 } = require('../../utils/httpErrors');

const serverUrl = getConfig().gladysGatewayServerUrl;

/**
* @description Login to Gladys Gateway.
* @param {string} email - User email.
Expand All @@ -21,7 +18,11 @@ async function login(email, password) {
this.gladysGatewayClient.disconnect();
}
// create a new instance of the client
this.gladysGatewayClient = new this.GladysGatewayClient({ cryptoLib: webcrypto, serverUrl, logger });
this.gladysGatewayClient = new this.GladysGatewayClient({
cryptoLib: webcrypto,
serverUrl: this.config.gladysGatewayServerUrl,
logger,
});
// We login with email/password to get two factor token
const loginResults = await this.gladysGatewayClient.login(email, password);
return loginResults;
Expand Down
25 changes: 18 additions & 7 deletions server/lib/gateway/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
const GladysGatewayClient = require('@gladysassistant/gladys-gateway-js');
const { webcrypto } = require('crypto');
const schedule = require('node-schedule');

const getConfig = require('../../utils/getConfig');
const logger = require('../../utils/logger');
const { EVENTS, JOB_TYPES } = require('../../utils/constants');
const { eventFunctionWrapper } = require('../../utils/functionsWrapper');

const serverUrl = getConfig().gladysGatewayServerUrl;

const { backup } = require('./gateway.backup');
const { forwardDeviceStateToAlexa } = require('./gateway.forwardDeviceStateToAlexa');
const { forwardDeviceStateToGoogleHome } = require('./gateway.forwardDeviceStateToGoogleHome');
Expand All @@ -34,12 +30,23 @@ const { refreshUserKeys } = require('./gateway.refreshUserKeys');
const { getEcowattSignals } = require('./gateway.getEcowattSignals');
const { openAIAsk } = require('./gateway.openAIAsk');

const Gateway = function Gateway(variable, event, system, sequelize, config, user, stateManager, serviceManager, job) {
const Gateway = function Gateway(
variable,
event,
system,
sequelize,
config,
user,
stateManager,
serviceManager,
job,
scheduler,
) {
this.variable = variable;
this.event = event;
this.system = system;
this.sequelize = sequelize;
this.schedule = schedule;
this.scheduler = scheduler;
this.config = config;
this.user = user;
this.stateManager = stateManager;
Expand All @@ -57,7 +64,11 @@ const Gateway = function Gateway(variable, event, system, sequelize, config, use
this.backupRandomInterval = 2 * 60 * 60 * 1000; // 2 hours
this.getLatestGladysVersionInitTimeout = 5 * 60 * 1000; // 5 minutes
this.GladysGatewayClient = GladysGatewayClient;
this.gladysGatewayClient = new GladysGatewayClient({ cryptoLib: webcrypto, serverUrl, logger });
this.gladysGatewayClient = new GladysGatewayClient({
cryptoLib: webcrypto,
serverUrl: config.gladysGatewayServerUrl,
logger,
});
this.backup = this.job.wrapper(JOB_TYPES.GLADYS_GATEWAY_BACKUP, this.backup.bind(this));

this.event.on(EVENTS.GATEWAY.CREATE_BACKUP, eventFunctionWrapper(this.backup.bind(this)));
Expand Down
15 changes: 13 additions & 2 deletions server/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,19 @@ function Gladys(params = {}) {
const calendar = new Calendar(service);
const scheduler = new Scheduler(event);
const weather = new Weather(service, event, message, house);
const gateway = new Gateway(variable, event, system, db.sequelize, config, user, stateManager, service, job);
const scene = new Scene(stateManager, event, device, message, variable, house, calendar, http, gateway);
const gateway = new Gateway(
variable,
event,
system,
db.sequelize,
config,
user,
stateManager,
service,
job,
scheduler,
);
const scene = new Scene(stateManager, event, device, message, variable, house, calendar, http, gateway, scheduler);

const gladys = {
version: '0.1.0', // todo, read package.json
Expand Down
4 changes: 2 additions & 2 deletions server/lib/scene/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const sunCalc = require('suncalc');
const schedule = require('node-schedule');

const queue = require('queue');
const { addScene } = require('./scene.addScene');
Expand Down Expand Up @@ -32,6 +31,7 @@ const SceneManager = function SceneManager(
calendar,
http,
gateway,
scheduler,
) {
this.stateManager = stateManager;
this.event = event;
Expand All @@ -49,7 +49,7 @@ const SceneManager = function SceneManager(
autostart: true,
});
this.sunCalc = sunCalc;
this.schedule = schedule;
this.scheduler = scheduler;
this.jobs = [];
this.event.on(EVENTS.TRIGGERS.CHECK, eventFunctionWrapper(this.checkTrigger.bind(this)));
this.event.on(EVENTS.ACTION.TRIGGERED, eventFunctionWrapper(this.executeSingleAction.bind(this)));
Expand Down
4 changes: 2 additions & 2 deletions server/lib/scene/scene.addScene.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function addScene(sceneRaw) {
// First, we had a trigger key, import to uniquely identify this trigger
trigger.key = uuid.v4();
if (trigger.type === EVENTS.TIME.CHANGED && trigger.scheduler_type !== 'interval') {
const rule = new this.schedule.RecurrenceRule();
const rule = {};
rule.tz = this.timezone;
switch (trigger.scheduler_type) {
case 'every-month':
Expand Down Expand Up @@ -68,7 +68,7 @@ function addScene(sceneRaw) {
default:
throw new BadParameters(`${trigger.scheduler_type} not supported`);
}
trigger.nodeScheduleJob = this.schedule.scheduleJob(rule, () =>
trigger.nodeScheduleJob = this.scheduler.scheduleJob(rule, () =>
this.event.emit(EVENTS.TRIGGERS.CHECK, trigger),
);
} else if (trigger.type === EVENTS.TIME.CHANGED && trigger.scheduler_type === 'interval') {
Expand Down
4 changes: 2 additions & 2 deletions server/lib/scene/scene.dailyUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async function dailyUpdate() {
.toDate();
logger.info(`Sunrise today is at ${sunriseHour}:${sunriseMinute} today, in your timezone = ${this.timezone}`);
logger.info(`Sunset today is at ${sunsetHour}:${sunsetMinute} today, in your timezone = ${this.timezone}`);
const sunriseJob = this.schedule.scheduleJob(sunriseTime, () =>
const sunriseJob = this.scheduler.scheduleJob(sunriseTime, () =>
this.event.emit(EVENTS.TRIGGERS.CHECK, {
type: EVENTS.TIME.SUNRISE,
house,
Expand All @@ -70,7 +70,7 @@ async function dailyUpdate() {
logger.info(`The sun rose this morning. Not scheduling for today.`);
}

const sunsetJob = this.schedule.scheduleJob(sunsetTime, () =>
const sunsetJob = this.scheduler.scheduleJob(sunsetTime, () =>
this.event.emit(EVENTS.TRIGGERS.CHECK, {
type: EVENTS.TIME.SUNSET,
house,
Expand Down
11 changes: 3 additions & 8 deletions server/lib/scene/scene.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,12 @@ async function init() {
});

// Recurrence rule (00:00 every day) to update sunrise/sunset time.
const rule = new this.schedule.RecurrenceRule();
rule.tz = this.timezone;
rule.hour = 0;
rule.minute = 0;
rule.second = 0;

this.schedule.scheduleJob(rule, this.dailyUpdate.bind(this));
const rule = { tz: this.timezone, hour: 0, minute: 0, second: 0 };
this.scheduler.scheduleJob(rule, this.dailyUpdate.bind(this));
await this.dailyUpdate();

// At every minute, check if calendar event is coming
this.schedule.scheduleJob('* * * * *', () => this.event.emit(EVENTS.CALENDAR.CHECK_IF_EVENT_IS_COMING));
this.scheduler.scheduleJob('* * * * *', () => this.event.emit(EVENTS.CALENDAR.CHECK_IF_EVENT_IS_COMING));

return plainScenes;
}
Expand Down
13 changes: 6 additions & 7 deletions server/lib/scheduler/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
const jobs = require('../../config/scheduler-jobs');
const schedule = require('node-schedule');

const { cancel } = require('./scheduler.cancel');
const { scheduleJob } = require('./scheduler.scheduleJob');
const { cancelJob } = require('./scheduler.cancelJob');
const { init } = require('./scheduler.init');
const { run } = require('./scheduler.run');

const Scheduler = function Scheduler(event) {
this.event = event;
this.jobs = jobs;
this.jobsScheduled = {};
this.nodeSchedule = schedule;
};

Scheduler.prototype.cancel = cancel;
Scheduler.prototype.scheduleJob = scheduleJob;
Scheduler.prototype.cancelJob = cancelJob;
Scheduler.prototype.init = init;
Scheduler.prototype.run = run;

module.exports = Scheduler;
18 changes: 0 additions & 18 deletions server/lib/scheduler/scheduler.cancel.js

This file was deleted.

13 changes: 13 additions & 0 deletions server/lib/scheduler/scheduler.cancelJob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @description Cancel job
* @param {string} jobName - Job name to cancel.
* @example
* scheduler.cancelJob('my-job');
*/
function cancelJob(jobName) {
this.nodeSchedule.cancelJob(jobName);
}

module.exports = {
cancelJob,
};
23 changes: 11 additions & 12 deletions server/lib/scheduler/scheduler.init.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
const logger = require('../../utils/logger');

const jobs = require('../../config/scheduler-jobs');

/**
* @description Init scheduler.
* @description Init event job scheduler.
* @example
* scheduler.init();
* scheduler.initEventJobs();
*/
async function init() {
function init() {
logger.debug(`Scheduler.init`);
// foreach job
this.jobs.forEach((job) => {
// if the job is already scheduled, we cancel it
if (this.jobsScheduled[job.name]) {
clearInterval(this.jobsScheduled[job.name]);
}
// then schedule it
this.jobsScheduled[job.name] = setInterval(() => {
this.run(job);
}, job.frequencyInSeconds * 1000);
jobs.forEach((job) => {
// schedule it
this.scheduleJob(job.rule, () => {
logger.debug(`Running job "${job.name}" at ${new Date()}`);
this.event.emit(job.event);
});
});
}

Expand Down
20 changes: 0 additions & 20 deletions server/lib/scheduler/scheduler.run.js

This file was deleted.

15 changes: 15 additions & 0 deletions server/lib/scheduler/scheduler.scheduleJob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @description Schedule new job.
* @param {Object|string|Date} rule - Rule to execute the job.
* @param {Function} method - The method to execute.
* @returns {Object} The scheduled job.
* @example
* scheduler.scheduleJob({ hour: 12 }, () => console.log('job is running'));
*/
function scheduleJob(rule, method) {
return this.nodeSchedule.scheduleJob(rule, method);
}

module.exports = {
scheduleJob,
};
20 changes: 17 additions & 3 deletions server/test/lib/gateway/GladysGatewayClientMock.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ const { fake } = require('sinon');

const GladysGatewayClientMock = function GladysGatewayClientMock() {
return {
login: fake.resolves({
two_factor_token: 'token',
}),
login: async (email, password) => {
return new Promise((resolve, reject) => {
if (password === 'pass403') {
const error = new Error();
error.response = { status: 403 };
reject(error);
return;
}
if (password === 'pass500') {
reject(new Error());
return;
}
resolve({
two_factor_token: 'token',
});
});
},
loginInstance: fake.resolves({}),
createInstance: fake.resolves({
instance: {
Expand Down
Binary file not shown.
Loading

0 comments on commit 6e65d6f

Please sign in to comment.