@yachteye/signalk-makkah-plugin
Version:
Add Salah and Sun times to the SignalK graph
336 lines (335 loc) • 18.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const SunAndPrayTime_1 = require("./SunAndPrayTime");
const trigonometric_1 = require("./trigonometric");
/**
* Makkah prayer times Plugin.
* @param {*} app The SignalK app.
* @returns The plugin object.
*/
module.exports = (app) => {
const plugin = {
id: 'signalk-makkah-plugin',
name: 'Add Salah and Sun times to the SignalK graph',
restartPlugin: null,
hourTimeout: null,
navigationInterval: null,
/**
* Start the plugin.
* @param {*} settings the configuration data entered via the Plugin Config screen.
* @param {*} restartPlugin a function that can be called by the plugin to restart itself.
*/
start: (settings, restartPlugin) => {
app.debug(`${plugin.id} starting...`);
plugin.restartPlugin = restartPlugin;
try {
plugin.meta(settings);
plugin.navigationInterval = setInterval(plugin.updateNavigationData, settings.interval * 1000);
const calculator = new SunAndPrayTime_1.SunAndPrayTimeCalculator();
calculator.setMethod(settings.method);
calculator.numIterations = 2;
setTimeout(() => {
plugin.work(settings, calculator);
}, 45 * 1000);
plugin.hourTimeout = plugin.callAfterEveryFullHour(plugin.work.bind(null, settings, calculator));
app.setPluginStatus(`Started (method=${settings.method}, calculation interval=${settings.interval} (seconds)).`);
}
catch (err) {
app.error(`error: ${err === null || err === void 0 ? void 0 : err.message}`);
app.setPluginError(`Error in start(): ${err === null || err === void 0 ? void 0 : err.message}`);
}
},
/**
* Stop the plugin.
*/
stop: () => {
if (plugin.hourTimeout) {
clearTimeout(plugin.hourTimeout);
plugin.hourTimeout = null;
}
if (plugin.navigationInterval) {
clearInterval(plugin.navigationInterval);
plugin.navigationInterval = null;
}
app.setPluginStatus(`Stopped.`);
},
work: (settings, calculator) => {
try {
const now = new Date();
const position = app.getSelfPath('navigation.position');
const dateTime = app.getSelfPath('navigation.datetime');
const tzOffsetData = app.getSelfPath('environment.time.timezoneOffset');
const latLon = plugin.getPosition(position);
if (latLon !== null && dateTime !== undefined && dateTime.value) {
let tzHourOffset = 0;
if (tzOffsetData !== undefined && tzOffsetData.value && typeof tzOffsetData.value === 'number') {
tzHourOffset = tzOffsetData.value / 60 / 60;
}
else {
app.debug(`No time zone information available: '${JSON.stringify(tzOffsetData)}'.`);
}
// Calculate the local date.
const utcDateTime = new Date(dateTime.value);
const localDateTime = new Date(utcDateTime.getTime() + tzHourOffset * 60 * 60 * 1000);
app.debug(`Local time is ${localDateTime.getUTCHours()}:${localDateTime.getUTCMinutes()}, UTC time is ${utcDateTime.getUTCHours()}:${utcDateTime.getUTCMinutes()}, UTC day=${localDateTime.getUTCDate()}, time zone=${tzHourOffset.toFixed(2)} decimal hours`);
const prayerTimes = calculator.getDatePrayerTimes(localDateTime.getUTCFullYear(), localDateTime.getUTCMonth() + 1, localDateTime.getUTCDate(), latLon.latitude, latLon.longitude, tzHourOffset);
const deltaSalah = {
context: 'vessels.self',
updates: [{
source: { label: `plugin.id` },
timestamp: now.toISOString(),
values: [{
path: settings.signalKPath,
value: {
fajr: prayerTimes.fajr,
dhuhr: prayerTimes.dhuhr,
asr: prayerTimes.asr,
maghrib: prayerTimes.maghrib,
isha: prayerTimes.isha,
sunrise: prayerTimes.sunrise,
sunset: prayerTimes.sunset,
// imsak: prayerTimes.imsak,
}
}],
}],
};
app.handleMessage(plugin.id, deltaSalah);
const localHours = localDateTime.getUTCHours() + (localDateTime.getUTCMinutes() / 60);
const result = calculator.GetSunTimes(localDateTime.getUTCFullYear(), localDateTime.getUTCMonth() + 1, localDateTime.getUTCDate(), latLon.latitude, latLon.longitude, tzHourOffset);
// Compute times for tomorrow, and correct the result for times that have already passed.
localDateTime.setUTCDate(localDateTime.getUTCDate() + 1);
const tomorrow = calculator.GetSunTimes(localDateTime.getUTCFullYear(), localDateTime.getUTCMonth() + 1, localDateTime.getUTCDate(), latLon.latitude, latLon.longitude, tzHourOffset);
if (result.sunrise !== null && result.sunrise <= localHours && tomorrow.sunrise !== null) {
result.sunrise = tomorrow.sunrise;
}
if (result.sunset !== null && result.sunset <= localHours && tomorrow.sunset !== null) {
result.sunset = tomorrow.sunset;
}
if (result.civicTwilightStart !== null && result.civicTwilightStart <= localHours && tomorrow.civicTwilightStart !== null) {
result.civicTwilightStart = tomorrow.civicTwilightStart;
}
if (result.civicTwilightEnd !== null && result.civicTwilightEnd <= localHours && tomorrow.civicTwilightEnd !== null) {
result.civicTwilightEnd = tomorrow.civicTwilightEnd;
}
const deltaSun = {
context: 'vessels.self',
updates: [{
source: { label: `plugin.id` },
timestamp: now.toISOString(),
values: [{
path: 'resources.astronomical.sunTimes',
value: {
sunrise: result.sunrise === null ? '' : calculator.floatToTime24(result.sunrise),
sunset: result.sunset === null ? '' : calculator.floatToTime24(result.sunset),
civicTwilightStart: result.civicTwilightStart === null ? '' : calculator.floatToTime24(result.civicTwilightStart),
civicTwilightEnd: result.civicTwilightEnd === null ? '' : calculator.floatToTime24(result.civicTwilightEnd),
}
}],
}],
};
app.handleMessage(plugin.id, deltaSun);
app.setPluginStatus(`Successfully calculated Salah/Sun times at ${now.toISOString()}.`);
app.debug(`Calculated Salah/Sun times for (lat=${latLon.latitude.toFixed(5)}, lon=${latLon.longitude.toFixed(5)}), tz=${tzHourOffset.toFixed(2)} decimal hours.`);
}
else {
app.setPluginStatus(`No position and/or dateTime data available at ${now.toISOString()}.`);
app.debug(`No position (${JSON.stringify(position)}) and/or dateTime (${JSON.stringify(dateTime)}) data available.`);
}
}
catch (err) {
app.error(`error: ${err === null || err === void 0 ? void 0 : err.message}`);
app.setPluginError(`Error calculating Salah/Sun times: ${err === null || err === void 0 ? void 0 : err.message}`);
}
},
updateNavigationData: () => {
// Values taken from https://en.wikipedia.org/wiki/Kaaba
const MakkahLatitude = 21.4225;
const MakkahLongitude = 39.8262;
try {
const now = new Date();
const position = app.getSelfPath('navigation.position');
const latLon = plugin.getPosition(position);
if (latLon !== null) {
const bearing = (0, trigonometric_1.bearingTo)(latLon.latitude, latLon.longitude, MakkahLatitude, MakkahLongitude);
const distance = (0, trigonometric_1.distanceBetweenMeters)(latLon.latitude, latLon.longitude, MakkahLatitude, MakkahLongitude);
const delta = {
context: 'vessels.self',
updates: [{
source: { label: `plugin.id` },
timestamp: now.toISOString(),
values: [
{
path: 'navigation.bearingToMakkah',
value: (0, trigonometric_1.degreeToRadian)(bearing),
},
{
path: 'navigation.distanceToMakkah',
value: Math.round(distance),
},
],
}],
};
app.handleMessage(plugin.id, delta);
app.setPluginStatus(`Calculated distance and direction to Makkah at ${now.toISOString()}.`);
}
else {
app.setPluginStatus(`No position data available at ${now.toISOString()}.`);
app.debug(`No position (${JSON.stringify(position)}) data available.`);
}
}
catch (err) {
app.error(`error: ${err === null || err === void 0 ? void 0 : err.message}`);
app.setPluginError(`Error calculating data: ${err === null || err === void 0 ? void 0 : err.message}`);
}
},
callAfterEveryFullHour: (functionToCall) => {
const now = new Date();
const nextHour = new Date();
nextHour.setHours(nextHour.getHours() + 1, 1, 0, 0);
const differenceMs = nextHour.getTime() - now.getTime();
return setTimeout(() => {
// Run it.
functionToCall();
// Schedule next run.
plugin.hourTimeout = plugin.callAfterEveryFullHour(functionToCall);
}, differenceMs);
},
getPosition: (skData) => {
if (skData !== undefined && skData !== null && skData.value && typeof skData.value.latitude === 'number' && typeof skData.value.longitude === 'number') {
return { latitude: skData.value.latitude, longitude: skData.value.longitude };
}
return null;
},
meta: (settings) => {
const metaDelta = {
context: "vessels.self",
updates: [
{
timestamp: new Date().toISOString(),
meta: [
{
path: "navigation.distanceToMakkah",
value: {
units: "m",
description: "Distance to Makkah",
displayName: "Distance to Makkah",
}
},
{
path: "navigation.bearingToMakkah",
value: {
units: "rad",
description: "Bearing/Direction to Makkah",
displayName: "Bearing/Direction to Makkah",
}
},
{
path: settings.signalKPath,
value: {
description: "The Salah/Prayer times of today, in 'hh:mm' format",
displayName: "The Salah/Prayer times of today",
properties: {
fajr: {
type: "string",
description: "The local time of today's Fajr",
example: "06:55",
},
dhuhr: {
type: "string",
description: "The local time of today's Dhuhr",
example: "13:46",
},
asr: {
type: "string",
description: "The local time of today's Asr",
example: "16:35",
},
maghrib: {
type: "string",
description: "The local time of today's Maghrib",
example: "19:01",
},
isha: {
type: "string",
description: "The local time of today's Isha",
example: "20:32",
},
sunrise: {
type: "string",
description: "The local time of today's sunrise",
example: "08:31",
},
sunset: {
type: "string",
description: "The local time of today's sunset",
example: "19:01",
},
// imsak: {
// type: "string",
// description: "The local time of today's Imsak",
// example: "06:45",
// },
},
}
},
{
path: 'resources.astronomical.sunTimes',
value: {
description: "The next Sun times (sunrise, sunset, etc.), in 'hh:mm' format",
displayName: "The next Sun times",
properties: {
sunrise: {
type: "string",
description: "The local time of the next sunrise",
example: "08:30",
},
sunset: {
type: "string",
description: "The local time of the next sunset",
example: "19:01",
},
civicTwilightStart: {
type: "string",
description: "The local time of the next civic twilight start",
example: "08:00",
},
civicTwilightEnd: {
type: "string",
description: "The local time of the next civic twilight end",
example: "19:31",
},
},
}
},
]
}
]
};
app.handleMessage(plugin.id, metaDelta);
},
schema: () => {
return {
type: 'object',
required: [],
properties: {
interval: {
type: 'number',
title: 'Interval to calculate the Makkah distance/direction (seconds)',
default: 50,
},
signalKPath: {
type: 'string',
title: 'The SignalK path to use.',
default: 'resources.astronomical.salahTimes',
},
method: {
type: 'string',
title: 'The calculation method: MWL, ISNA, Egypt, Makkah, Karachi, Tehran or Jafari.',
default: 'MWL',
},
}
};
},
};
return plugin;
};