Skip to content

Instantly share code, notes, and snippets.

@ParoTheParrot
Forked from HendrikRunte/dawn2dusk.js
Last active October 27, 2020 15:02
Show Gist options
  • Save ParoTheParrot/c545b0aebadff4557937152f9c2c535b to your computer and use it in GitHub Desktop.
Save ParoTheParrot/c545b0aebadff4557937152f9c2c535b to your computer and use it in GitHub Desktop.
Scriptable.app widget displaying the exact time of today's sunrise and sunset. Which comes in handy in the wintertime … #scriptable
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: orange; icon-glyph: sun-2;
///////////////////////////////////////////////////////////////////////
// dawn2dusk.js
// Take it and have fun.
// Hendrik Runte, Oct 24, 2020.
///////////////////////////////////////////////////////////////////////
// Extending the JavaScritp Date object.
// Usage:
// const sunriseDateObject = new Date().sunrise(lat, long);
// const sunsetDateObject = new Date().sunrise(lat, long);
// All the other methods just help.
Date.prototype.sunrise = function (latitude, longitude, zenith) {
return this.setSun(latitude, longitude, true, zenith);
};
Date.prototype.sunset = function (latitude, longitude, zenith) {
return this.setSun(latitude, longitude, false, zenith);
};
Date.prototype.setSun = function (latitude, longitude, isSunrise, zenith) {
zenith = zenith || 90.8333;
const DEGREES_PER_HOUR = 360 / 24;
const hoursFromMeridian = longitude / DEGREES_PER_HOUR;
const dayOfYear = this.getDayOfYear();
const approxTimeOfEventInDays = isSunrise
? dayOfYear + (6 - hoursFromMeridian) / 24
: dayOfYear + (18 - hoursFromMeridian) / 24;
const sunMeanAnomaly = 0.9856 * approxTimeOfEventInDays - 3.289;
const sunTrueLongitude = Math.mod(
sunMeanAnomaly +
1.916 * Math.sinDeg(sunMeanAnomaly) +
0.02 * Math.sinDeg(2 * sunMeanAnomaly) +
282.634,
360
);
const ascension = 0.91764 * Math.tanDeg(sunTrueLongitude);
let rightAscension = (360 / (2 * Math.PI)) * Math.atan(ascension);
rightAscension = Math.mod((360 / (2 * Math.PI)) * Math.atan(ascension), 360);
const lQuadrant = Math.floor(sunTrueLongitude / 90) * 90;
const raQuadrant = Math.floor(rightAscension / 90) * 90;
rightAscension = rightAscension + (lQuadrant - raQuadrant);
rightAscension /= DEGREES_PER_HOUR;
const sinDec = 0.39782 * Math.sinDeg(sunTrueLongitude);
const cosDec = Math.cosDeg(Math.asinDeg(sinDec));
const cosLocalHourAngle =
(Math.cosDeg(zenith) - sinDec * Math.sinDeg(latitude)) /
(cosDec * Math.cosDeg(latitude));
const localHourAngle = Math.acosDeg(cosLocalHourAngle);
const localHour = isSunrise
? (360 - localHourAngle) / DEGREES_PER_HOUR
: localHourAngle / DEGREES_PER_HOUR;
const localMeanTime =
localHour + rightAscension - 0.06571 * approxTimeOfEventInDays - 6.622;
let time = localMeanTime - longitude / DEGREES_PER_HOUR;
time = Math.mod(time, 24);
const midnight = new Date(0);
midnight.setUTCFullYear(this.getUTCFullYear());
midnight.setUTCMonth(this.getUTCMonth());
midnight.setUTCDate(this.getUTCDate());
const milli = midnight.getTime() + time * 60 * 60 * 1000;
return new Date(milli);
};
// Utility functions
Date.prototype.getDayOfYear = function () {
return Math.ceil((this - new Date(this.getFullYear(), 0, 1)) / 86400000);
};
Math.degToRad = function (num) {
return (num * Math.PI) / 180;
};
Math.radToDeg = function (radians) {
return (radians * 180.0) / Math.PI;
};
Math.sinDeg = function (deg) {
return Math.sin((deg * 2.0 * Math.PI) / 360.0);
};
Math.acosDeg = function (x) {
return (Math.acos(x) * 360.0) / (2 * Math.PI);
};
Math.asinDeg = function (x) {
return (Math.asin(x) * 360.0) / (2 * Math.PI);
};
Math.tanDeg = function (deg) {
return Math.tan((deg * 2.0 * Math.PI) / 360.0);
};
Math.cosDeg = function (deg) {
return Math.cos((deg * 2.0 * Math.PI) / 360.0);
};
Math.mod = function (a, b) {
let result = a % b;
if (result < 0) {
result += b;
}
return result;
};
///////////////////////////////////////////////////////////////////////
// Here comes the actual Scriptable widget stuff.
///////////////////////////////////////////////////////////////////////
const widget = await createWidget();
if (!config.runsInWidget) {
await widget.presentSmall();
}
Script.setWidget(widget);
Script.complete();
async function createWidget(items) {
const list = new ListWidget();
const location = await getLocation();
const gradient = new LinearGradient();
gradient.locations = [0, 1];
gradient.colors = [new Color('#2D1925'), new Color('#402938')];
list.backgroundGradient = gradient;
const today = new Date();
const header = list.addText('Sonnenlauf'.toUpperCase());
const sunrise = today.sunrise(location.latitude, location.longitude);
const sunset = today.sunset(location.latitude, location.longitude);
header.font = Font.mediumSystemFont(11);
header.textColor = Color.white();
list.addSpacer(12);
// Sunrise
const sunriseStack = list.addStack();
addSymbol({
symbol: 'sunrise.fill',
stack: sunriseStack,
color: Color.orange(),
size: 28,
});
sunriseStack.addSpacer();
const sunriseLabel = sunriseStack.addText(
` ${('0' + sunrise.getHours()).slice(-2)}:${(
'0' + sunrise.getMinutes()
).slice(-2)}`
);
sunriseLabel.font = Font.boldSystemFont(28);
sunriseLabel.textColor = Color.orange();
// Sunset
const sunsetStack = list.addStack();
addSymbol({
symbol: 'sunset.fill',
stack: sunsetStack,
color: Color.orange(),
size: 28,
});
sunsetStack.addSpacer();
const sunsetLabel = sunsetStack.addText(
` ${('0' + sunset.getHours()).slice(-2)}:${(
'0' + sunset.getMinutes()
).slice(-2)}`
);
sunsetLabel.font = Font.boldSystemFont(28);
sunsetLabel.textColor = Color.orange();
list.addSpacer(12);
// Geo location:
const geoStack = list.addStack();
addSymbol({
symbol: 'location.fill',
stack: geoStack,
color: Color.white(),
size: 11,
});
geoStack.addSpacer();
const geoLabel = geoStack.addText(
` ${location.latitude
.toString()
.substr(0, 6)}, ${location.longitude.toString().substr(0, 6)}`
);
geoLabel.font = Font.thinSystemFont(11);
geoLabel.textColor = Color.white();
// render
return list;
}
// Locate yourself or you params.
async function getLocation() {
try {
if (args.widgetParameter) {
const fixedCoordinates = args.widgetParameter.split(',').map(parseFloat);
return { latitude: fixedCoordinates[0], longitude: fixedCoordinates[1] };
} else {
Location.setAccuracyToThreeKilometers();
return await Location.current();
}
} catch (e) {
return null;
}
}
// Helps adding icons from SF Symbols.
function addSymbol({
symbol = 'applelogo',
stack,
color = Color.white(),
size = 20,
}) {
const sym = SFSymbol.named(symbol);
const icon = stack.addImage(sym.image);
icon.tintColor = color;
icon.imageSize = new Size(size, size);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment