content/hifi-content/zfox/temp/roomScheduleServer.js
2022-02-14 02:04:11 +01:00

465 lines
No EOL
17 KiB
JavaScript

// roomScheduleServer.js
//
// Created by Mark Brosche on 4/3/2019
// Handed off to Milad Nazeri on 5-15-2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
(function() {
var CHANNEL = "HiFi.Google.Calendar";
var TOKEN = 0;
var EXPIRE_TIME = 1;
var TIMEZONE_OFFSET = 2;
var TIMEZONE_NAME = 3;
var ADDRESS = 4;
var INTERVAL_FREQUENCY_MS = 60000;
var EXPIRY_BUFFER_MS = 300000;
var HOURS_PER_DAY = 24;
var NOON_HR = 12;
var SIGN_TEXT_COLOR_AVAILABLE = [168, 255, 168];
var SIGN_TEXT_COLOR_INUSE = [255, 168, 168];
var SIGN_BACKGROUND_COLOR_AVAILABLE = [125, 255, 125];
var SIGN_BACKGROUND_COLOR_INUSE = [255, 125, 125];
var REFRESH_TIMEOUT = 1920000; // 32 minutes
var SCRIPT_NAME = "roomScheduleServer.js";
var that = this;
this.remotelyCallable = [
"refreshToken",
"secondarySync",
"updateTimeZoneInfo"
];
this.preload = function(entityID) {
that.entityID = entityID;
that.entityProperties = Entities.getEntityProperties(that.entityID, ['privateUserData', 'userData', 'name']);
var userData;
var privateUserData;
try {
if (that.entityProperties.userData.length > 0) {
userData = JSON.parse(that.entityProperties.userData);
} else {
userData = {};
}
if (that.entityProperties.privateUserData.length > 0) {
privateUserData = JSON.parse(that.entityProperties.privateUserData);
} else {
privateUserData = {};
}
} catch (e) {
console.log(e, "Could not parse userData");
return;
}
that.request = Script.require('https://hifi-content.s3.amazonaws.com/Experiences/Releases/modules/request/v1.0/request.js').request;
that.room = {
"eventList": []
};
that.TOKEN_SERVER_ID = userData.tokenServerID;
that.secondaryRoomSchedule = userData.secondaryRoomSchedule;
that.roomScheduleID = userData.roomScheduleID;
that.roomOccupantsListID = userData.roomOccupantsListID;
that.roomColorID = userData.roomColorID;
that.roomColorOccupantsID = userData.roomColorOccupantsID;
that.roomClockID = userData.roomClockID;
that.roomEntityIDs = [
that.roomScheduleID,
that.roomColorID,
that.roomColorOccupantsID,
that.roomOccupantsListID
];
that.timezoneName = userData.timezoneName;
that.address = userData.address;
that.expireTime = userData.expireTime;
that.isSecondarySchedule = userData.isSecondarySchedule;
that.token = privateUserData.token;
that.timezoneOffset = +userData.timezoneOffset;
that.clearEventList(that.entityID);
that.updateSignColor(that.roomEntityIDs[1], [true]);
that.updateSignColor(that.roomEntityIDs[2], [true]);
that.sentAlready = false;
if (that.secondaryRoomSchedule){
Entities.callEntityMethod(that.secondaryRoomSchedule, "updateTimeZoneInfo", [that.timezoneName, that.timezoneOffset ]);
}
if (that.token && !that.isSecondarySchedule) {
that.googleRequest(that.token);
}
if (!that.isSecondarySchedule) {
Entities.callEntityMethod(that.roomClockID, "refreshTimezone", [that.timezoneName, that.timezoneOffset]);
}
};
// Make sure the secondary calendar has the correct timezone for it's ui update
that.updateTimeZoneInfo = function(id, params) {
var userData = Entities.getEntityProperties(that.entityID, ['userData']).userData;
try {
if (userData.length > 0) {
userData = JSON.parse(userData);
} else {
userData = {};
}
} catch (e) {
console.log("trouble parsing userData");
}
userData.timezoneName = params[0];
userData.timezoneOffset = params[1];
Entities.editEntity(that.entityID, { userData: JSON.stringify(userData) });
};
// If this is a secondary display, then this will update the UI without making a call
this.secondarySync = function(id, params) {
var events = JSON.parse(params[0]);
that.createEvents(events);
};
// Called when the token server has a new token to give the calendar schedules
this.refreshToken = function(id, params) {
if (that.entityID === id) {
that.entityProperties = Entities.getEntityProperties(that.entityID, ['userData', 'name']);
if (that.interval) {
Script.clearInterval(that.interval);
that.interval = false;
}
var userData;
var privateUserData = {};
if (that.entityProperties.userData.length !== 0) {
try {
userData = JSON.parse(that.entityProperties.userData);
} catch (e) {
console.log(e, "no userData found");
}
} else {
userData = {};
}
that.token = privateUserData.token = params[TOKEN];
that.expireTime = userData.expireTime = params[EXPIRE_TIME];
that.timezoneOffset = userData.timezoneOffset = +params[TIMEZONE_OFFSET];
that.timezoneName = userData.timezoneName = params[TIMEZONE_NAME];
that.address = userData.address = params[ADDRESS];
Entities.editEntity(that.entityID, {
userData: JSON.stringify(userData),
privateUserData: JSON.stringify(privateUserData)
});
that.sentAlready = false;
Entities.callEntityMethod(that.roomClockID, "refreshTimezone", [that.timezoneName, that.timezoneOffset]);
that.googleRequest(that.token);
}
};
// Prepare the API request to google
this.googleRequest = function(token) {
var tomorrowMidnight = new Date();
tomorrowMidnight.setHours(HOURS_PER_DAY, 0, 0, 0);
that.scheduleURL = encodeURI("https://www.googleapis.com/calendar/v3/calendars/" +
that.address +
"/events?&timeMin=" + (new Date()).toISOString() +
"&timeMax=" + tomorrowMidnight.toISOString() +
"&showDeleted=" + false +
"&singleEvents=" + true +
"&maxResults=2" +
"&orderBy=startTime" +
"&access_token=" + token);
if (!that.interval) {
that.requestScheduleData(that.scheduleURL);
} else {
Script.clearInterval(that.interval);
that.requestScheduleData(that.scheduleURL);
}
};
// Call the google API and get back the list of events to post
var RETRY_TIMEOUT = 5000;
this.requestScheduleData = function(scheduleURL) {
that.request(scheduleURL, function (error, response) {
if (error) {
Messages.sendMessage(CHANNEL, JSON.stringify({
type: "ERROR",
entityName: that.entityProperties.name,
errorMessage: error,
actionAttempted: "Requesting schedule from Google - Initial"
}));
if (that.interval) {
Script.clearInterval(that.interval);
that.interval = false;
}
return;
} else {
var events = response.items;
that.createEvents(events);
}
});
that.interval = Script.setInterval(function() {
if ((new Date()).valueOf() > (that.expireTime - EXPIRY_BUFFER_MS) && !that.sentAlready) {
that.sentAlready = true;
Entities.callEntityMethod(that.TOKEN_SERVER_ID, "tokenCheck", [that.token, that.entityID]);
} else if ((new Date()).valueOf() > (that.expireTime)) {
Messages.sendMessage(CHANNEL, JSON.stringify({
type: "ERROR",
entityName: that.entityProperties.name,
errorMessage: "Token expired without refreshing",
actionAttempted: "Requesting refresh token from Token Server"
}));
if (that.interval) {
Script.clearInterval(that.interval);
that.interval = false;
}
return;
}
that.request(scheduleURL, function (error, response) {
if (error) {
if (that.interval) {
Script.clearInterval(that.interval);
that.interval = false;
}
Messages.sendMessage(CHANNEL, JSON.stringify({
type: "ERROR",
entityName: that.entityProperties.name,
errorMessage: error,
actionAttempted: "Requesting schedule from Google - Loop"
}));
return;
} else {
var events = response.items;
that.createEvents(events);
}
});
}, INTERVAL_FREQUENCY_MS);
};
// handle preparing the event post
this.createEvents = function(events) {
that.clearEventList(that.entityID);
if (events.length > 0) {
for (var i = 0; i < events.length; i++) {
var event = events[i];
var start = event.start.dateTime;
var end = event.end.dateTime;
var summary = event.summary;
if (!(start && end)) {
console.log("Event received without a start and end dateTime!");
continue;
}
var startTimestamp = that.googleDateToUTCDate(start);
var endTimestamp = that.googleDateToUTCDate(end);
that.postEvents(that.entityID, summary, startTimestamp, endTimestamp);
}
} else {
Entities.editEntity(that.entityID, {text: "Nothing on the schedule for this room today."});
}
if (that.secondaryRoomSchedule){
Entities.callEntityMethod(that.secondaryRoomSchedule, "updateTimeZoneInfo", [that.timezoneName, that.timezoneOffset ]);
Entities.callEntityMethod(that.secondaryRoomSchedule, "secondarySync", [JSON.stringify(events)]);
}
Entities.callEntityMethod(that.roomClockID, "refreshTimezone", [that.timezoneName, that.timezoneOffset]);
};
// Format the given google date from the response
var googleDate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]\d{2}):(\d{2})$/;
var MINS_PER_HOUR = 60;
this.googleDateToUTCDate = function(d) {
var m = googleDate.exec(d);
var year = +m[1];
var month = +m[2];
var day = +m[3];
var hour = +m[4];
var minute = +m[5];
var second = +m[6];
var msec = 0;
var tzHour = +m[7];
var tzMin = +m[8];
var tzOffset = new Date().getTimezoneOffset() + tzHour * MINS_PER_HOUR + tzMin;
var date = new Date(Date.UTC(year, month - 1, day, hour, minute - tzOffset, second, msec));
return date;
};
// Update the calendar UI when we have the latest calendar information
this.postEvents = function(id, summary, start, end) {
if (id === that.entityID) {
var printedSchedule = '';
var startTimestamp = start;
var endTimestamp = end;
var tempEvent = {
summary: summary,
startTimestamp: startTimestamp,
endTimestamp: endTimestamp
};
that.room.eventList.push(tempEvent);
that.room.eventList.forEach(function(event) {
var startHours;
var endHours;
var eventStartHour = +event.startTimestamp.getHours();
var eventEndHour = +event.endTimestamp.getHours();
if (eventStartHour + that.timezoneOffset <= 0) {
startHours = eventStartHour + that.timezoneOffset + NOON_HR;
} else if (eventStartHour + that.timezoneOffset <= (NOON_HR)) {
startHours = eventStartHour + that.timezoneOffset;
} else {
startHours = eventStartHour + that.timezoneOffset - NOON_HR;
}
if (eventEndHour + that.timezoneOffset <= 0) {
endHours = eventEndHour + that.timezoneOffset + NOON_HR;
} else if (eventEndHour + that.timezoneOffset <= (NOON_HR)) {
endHours = eventEndHour + that.timezoneOffset;
} else {
endHours = eventEndHour + that.timezoneOffset - NOON_HR;
}
var startAmPm;
var endAmPm;
if (eventStartHour + that.timezoneOffset < 0) {
startAmPm = "pm";
} else if (eventStartHour + that.timezoneOffset < (NOON_HR)) {
startAmPm = "am";
} else {
startAmPm = "pm";
}
if (eventEndHour + that.timezoneOffset < 0) {
endAmPm = "pm";
} else if (eventEndHour + that.timezoneOffset < (NOON_HR)) {
endAmPm = "am";
} else {
endAmPm = "pm";
}
printedSchedule =
printedSchedule +
event.summary +
"\n" +
startHours +
':' +
(event.startTimestamp.getMinutes() < 10 ?
"0" + event.startTimestamp.getMinutes() :
event.startTimestamp.getMinutes()) +
' ' +
startAmPm +
' - ' +
endHours +
':' +
(event.endTimestamp.getMinutes() < 10 ?
"0" + event.endTimestamp.getMinutes() :
event.endTimestamp.getMinutes()) +
' ' +
endAmPm +
' ' + that.timezoneName + '\n\n';
});
Entities.editEntity(id, {text: printedSchedule});
that.setBusyLight();
}
};
// Erase the current event list
this.clearEventList = function(id) {
that.room.eventList = [];
Entities.editEntity(id, {text: ""});
};
// Switch the color occupants the top color of the schedule to show if it is in use or available
this.setBusyLight = function() {
var now = new Date();
var isAvailable = true;
if (typeof that.room.eventList[0] === "object") {
var endValue = that.room.eventList[0].endTimestamp;
var startValue = that.room.eventList[0].startTimestamp;
if (now <= endValue && now >= startValue) {
isAvailable = false;
} else {
isAvailable = true;
}
} else {
isAvailable = true;
}
that.updateSignColor(that.roomEntityIDs[1], [isAvailable]);
that.updateSignColor(that.roomEntityIDs[2], [isAvailable]);
};
// Handles whenever the color occupants and the top availabilty/in use color bar need to be changed
this.updateSignColor = function(id, params) {
if (id === that.roomEntityIDs[1]) {
if (params[0] === true) {
Entities.editEntity(id, {
"text": 'AVAILABLE',
"textColor": [0,0,0],
"textAlpha": 1,
"backgroundColor": SIGN_BACKGROUND_COLOR_AVAILABLE
});
} else {
Entities.editEntity(id, {
"text": 'IN USE',
"textColor": [0,0,0],
"textAlpha": 1,
"backgroundColor": SIGN_BACKGROUND_COLOR_INUSE
});
}
} else {
if (params[0] === true) {
Entities.editEntity(id, {
"text": 'occupants',
"textColor": SIGN_TEXT_COLOR_AVAILABLE
});
} else {
Entities.editEntity(id, {
"text": 'occupants',
"textColor": SIGN_TEXT_COLOR_INUSE
});
}
}
};
this.unload = function() {
if (that.interval) {
Script.clearInterval(that.interval);
that.interval = false;
}
};
});