Merge branch 'master' into unlibrify-message-widgets

pull/2376/head
Gordon Williams 2022-12-09 10:34:38 +00:00 committed by GitHub
commit cd785f34aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 632 additions and 913 deletions

View File

@ -1,2 +1,3 @@
0.01: Create astrocalc app 0.01: Create astrocalc app
0.02: Store last GPS lock, can be used instead of waiting for new GPS on start 0.02: Store last GPS lock, can be used instead of waiting for new GPS on start
0.03: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -9,7 +9,7 @@
* Calculate the Sun and Moon positions based on watch GPS and display graphically * Calculate the Sun and Moon positions based on watch GPS and display graphically
*/ */
const SunCalc = require("suncalc.js"); const SunCalc = require("suncalc"); // from modules folder
const storage = require("Storage"); const storage = require("Storage");
const LAST_GPS_FILE = "astrocalc.gps.json"; const LAST_GPS_FILE = "astrocalc.gps.json";
let lastGPS = (storage.readJSON(LAST_GPS_FILE, 1) || null); let lastGPS = (storage.readJSON(LAST_GPS_FILE, 1) || null);
@ -385,4 +385,4 @@ function init() {
} }
let m; let m;
init(); init();

View File

@ -1,7 +1,7 @@
{ {
"id": "astrocalc", "id": "astrocalc",
"name": "Astrocalc", "name": "Astrocalc",
"version": "0.02", "version": "0.03",
"description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.", "description": "Calculates interesting information on the sun and moon cycles for the current day based on your location.",
"icon": "astrocalc.png", "icon": "astrocalc.png",
"tags": "app,sun,moon,cycles,tool,outdoors", "tags": "app,sun,moon,cycles,tool,outdoors",
@ -9,7 +9,6 @@
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"astrocalc.app.js","url":"astrocalc-app.js"}, {"name":"astrocalc.app.js","url":"astrocalc-app.js"},
{"name":"suncalc.js","url":"suncalc.js"},
{"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true}, {"name":"astrocalc.img","url":"astrocalc-icon.js","evaluate":true},
{"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true}, {"name":"first-quarter.img","url":"first-quarter-icon.js","evaluate":true},
{"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true}, {"name":"last-quarter.img","url":"last-quarter-icon.js","evaluate":true},

View File

@ -1,328 +0,0 @@
/*
(c) 2011-2015, Vladimir Agafonkin
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
https://github.com/mourner/suncalc
*/
(function () { 'use strict';
// shortcuts for easier to read formulas
var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
asin = Math.asin,
atan = Math.atan2,
acos = Math.acos,
rad = PI / 180;
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
// date/time constants and conversions
var dayMs = 1000 * 60 * 60 * 24,
J1970 = 2440588,
J2000 = 2451545;
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j) { return (j + 0.5 - J1970) * dayMs; }
function toDays(date) { return toJulian(date) - J2000; }
// general calculations for position
var e = rad * 23.4397; // obliquity of the Earth
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
function astroRefraction(h) {
if (h < 0) // the following formula works for positive altitudes only.
h = 0; // if h = -0.08901179 a div/0 would occur.
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
}
// general sun calculations
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
function eclipticLongitude(M) {
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
P = rad * 102.9372; // perihelion of the Earth
return M + C + P + PI;
}
function sunCoords(d) {
var M = solarMeanAnomaly(d),
L = eclipticLongitude(M);
return {
dec: declination(L, 0),
ra: rightAscension(L, 0)
};
}
var SunCalc = {};
// calculates sun position for a given date and latitude/longitude
SunCalc.getPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = sunCoords(d),
H = siderealTime(d, lw) - c.ra;
return {
azimuth: azimuth(H, phi, c.dec),
altitude: altitude(H, phi, c.dec)
};
};
// sun times configuration (angle, morning name, evening name)
var times = SunCalc.times = [
[-0.833, 'sunrise', 'sunset' ],
[ -0.3, 'sunriseEnd', 'sunsetStart' ],
[ -6, 'dawn', 'dusk' ],
[ -12, 'nauticalDawn', 'nauticalDusk'],
[ -18, 'nightEnd', 'night' ],
[ 6, 'goldenHourEnd', 'goldenHour' ]
];
// adds a custom time to the times config
SunCalc.addTime = function (angle, riseName, setName) {
times.push([angle, riseName, setName]);
};
// calculations for sun times
var J0 = 0.0009;
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
// returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L) {
var w = hourAngle(h, phi, dec),
a = approxTransit(w, lw, n);
return solarTransitJ(a, M, L);
}
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
SunCalc.getTimes = function (date, lat, lng, height) {
height = height || 0;
var lw = rad * -lng,
phi = rad * lat,
dh = observerAngle(height),
d = toDays(date),
n = julianCycle(d, lw),
ds = approxTransit(0, lw, n),
M = solarMeanAnomaly(ds),
L = eclipticLongitude(M),
dec = declination(L, 0),
Jnoon = solarTransitJ(ds, M, L),
i, len, time, h0, Jset, Jrise;
var result = {
solarNoon: new Date(fromJulian(Jnoon)),
nadir: new Date(fromJulian(Jnoon - 0.5))
};
for (i = 0, len = times.length; i < len; i += 1) {
time = times[i];
h0 = (time[0] + dh) * rad;
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = new Date(fromJulian(Jrise) - (dayMs / 2));
result[time[2]] = new Date(fromJulian(Jset) + (dayMs / 2));
}
return result;
};
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
M = rad * (134.963 + 13.064993 * d), // mean anomaly
F = rad * (93.272 + 13.229350 * d), // mean distance
l = L + rad * 6.289 * sin(M), // longitude
b = rad * 5.128 * sin(F), // latitude
dt = 385001 - 20905 * cos(M); // distance to the moon in km
return {
ra: rightAscension(l, b),
dec: declination(l, b),
dist: dt
};
}
SunCalc.getMoonPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = moonCoords(d),
H = siderealTime(d, lw) - c.ra,
h = altitude(H, phi, c.dec),
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
h = h + astroRefraction(h); // altitude correction for refraction
return {
azimuth: azimuth(H, phi, c.dec),
altitude: h,
distance: c.dist,
parallacticAngle: pa
};
};
// calculations for illumination parameters of the moon,
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
// Function updated from gist: https://gist.github.com/endel/dfe6bb2fbe679781948c
SunCalc.getMoonIllumination = function (date) {
let month = date.getMonth();
let year = date.getFullYear();
let day = date.getDate();
let c = 0;
let e = 0;
let jd = 0;
let b = 0;
if (month < 3) {
year--;
month += 12;
}
++month;
c = 365.25 * year;
e = 30.6 * month;
jd = c + e + day - 694039.09; // jd is total days elapsed
jd /= 29.5305882; // divide by the moon cycle
b = parseInt(jd); // int(jd) -> b, take integer part of jd
jd -= b; // subtract integer part to leave fractional part of original jd
b = Math.round(jd * 8); // scale fraction from 0-8 and round
if (b >= 8) b = 0; // 0 and 8 are the same so turn 8 into 0
return {phase: b};
};
function hoursLater(date, h) {
return new Date(date.valueOf() + h * dayMs / 24);
}
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
var t = date;
if (inUTC) t.setUTCHours(0, 0, 0, 0);
else t.setHours(0, 0, 0, 0);
var hc = 0.133 * rad,
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
for (var i = 1; i <= 24; i += 2) {
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
a = (h0 + h2) / 2 - h1;
b = (h2 - h0) / 2;
xe = -b / (2 * a);
ye = (a * xe + b) * xe + h1;
d = b * b - 4 * a * h1;
roots = 0;
if (d >= 0) {
dx = Math.sqrt(d) / (Math.abs(a) * 2);
x1 = xe - dx;
x2 = xe + dx;
if (Math.abs(x1) <= 1) roots++;
if (Math.abs(x2) <= 1) roots++;
if (x1 < -1) x1 = x2;
}
if (roots === 1) {
if (h0 < 0) rise = i + x1;
else set = i + x1;
} else if (roots === 2) {
rise = i + (ye < 0 ? x2 : x1);
set = i + (ye < 0 ? x1 : x2);
}
if (rise && set) break;
h0 = h2;
}
var result = {};
if (rise) result.rise = hoursLater(t, rise);
if (set) result.set = hoursLater(t, set);
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
return result;
};
// export as Node module / AMD module / browser variable
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc;
else if (typeof define === 'function' && define.amd) define(SunCalc);
else global.SunCalc = SunCalc;
}());

View File

@ -1 +1,3 @@
0.01: New App! 0.01: New App!
0.02: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps
Add a 'time' clockinfo that also displays a percentage of day left

View File

@ -1,32 +1,68 @@
(function() { (function() {
// get today's sunlight times for lat/lon // get today's sunlight times for lat/lon
var sunrise, sunset; var sunrise, sunset, date;
var SunCalc = require("suncalc"); // from modules folder
const locale = require("locale");
function calculate() { function calculate() {
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
const locale = require("locale");
var location = require("Storage").readJSON("mylocation.json",1)||{}; var location = require("Storage").readJSON("mylocation.json",1)||{};
location.lat = location.lat||51.5072; location.lat = location.lat||51.5072;
location.lon = location.lon||0.1276; location.lon = location.lon||0.1276; // London
location.location = location.location||"London"; date = new Date(Date.now());
var times = SunCalc.getTimes(new Date(), location.lat, location.lon); var times = SunCalc.getTimes(date, location.lat, location.lon);
sunrise = locale.time(times.sunrise,1); sunrise = times.sunrise;
sunset = locale.time(times.sunset,1); sunset = times.sunset;
/* do we want to re-calculate this every day? Or we just assume /* do we want to re-calculate this every day? Or we just assume
that 'show' will get called once a day? */ that 'show' will get called once a day? */
} }
function show() {
this.interval = setTimeout(()=>{
this.emit("redraw");
this.interval = setInterval(()=>{
this.emit("redraw");
}, 60000);
}, 60000 - (Date.now() % 60000));
}
function hide() {
clearInterval(this.interval);
this.interval = undefined;
}
return { return {
name: "Bangle", name: "Bangle",
items: [ items: [
{ name : "Sunrise", { name : "Sunrise",
get : () => ({ text : sunrise, get : () => { calculate();
img : atob("GBiBAAAAAAAAAAAAAAAYAAA8AAB+AAD/AAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }), return { text : locale.time(sunrise,1),
show : calculate, hide : () => {} img : atob("GBiBAAAAAAAAAAAAAAAYAAA8AAB+AAD/AAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }},
show : show, hide : hide
}, { name : "Sunset", }, { name : "Sunset",
get : () => ({ text : sunset, get : () => { calculate();
img : atob("GBiBAAAAAAAAAAAAAAB+AAA8AAAYAAAYAAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }), return { text : locale.time(sunset,1),
show : calculate, hide : () => {} img : atob("GBiBAAAAAAAAAAAAAAB+AAA8AAAYAAAYAAAAAAAAAAAAAAAYAAAYAAQYIA4AcAYAYAA8AAB+AAD/AAH/gD///D///AAAAAAAAAAAAA==") }},
show : show, hide : hide
}, { name : "Time", // Time in day (uses v/min/max to show percentage through day)
get : () => {
calculate();
let day = true;
let v = 0;
let d = date.getTime();
let dayLength = sunset.getTime()-sunrise.getTime();
if (d < sunrise.getTime()) {
day = false; // early morning
v = 100 - Math.round(100 * (sunrise.getTime()-d) / (86400000-dayLength));
} else if (d > sunset.getTime()) {
day = false; // evening
v = Math.round(100 * (d-sunset.getTime()) / (86400000-dayLength));
} else { // day!
v = Math.round(100 * (d-sunrise.getTime()) / dayLength);
}
return { text : locale.time(date,1), v : v, min : 0, max : 100,
img : day ? atob("GBiBAAAYAAAYAAAYAAgAEBwAOAx+MAD/AAH/gAP/wAf/4Af/4Of/5+f/5wf/4Af/4AP/wAH/gAD/AAx+MBwAOAgAEAAYAAAYAAAYAA==") : atob("GBiBAAfwAA/8AAP/AAH/gAD/wAB/wAB/4AA/8AA/8AA/8AAf8AAf8AAf8AAf8AA/8AA/8AA/4AB/4AB/wAD/wAH/gAf/AA/8AAfwAA==")
}
},
show : show, hide : hide
} }
] ]
}; };

View File

@ -1,6 +1,6 @@
{ "id": "clkinfosunrise", { "id": "clkinfosunrise",
"name": "Sunrise Clockinfo", "name": "Sunrise Clockinfo",
"version":"0.01", "version":"0.02",
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays sunrise and sunset based on the location from the 'My Location' app", "description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays sunrise and sunset based on the location from the 'My Location' app",
"icon": "app.png", "icon": "app.png",
"type": "clkinfo", "type": "clkinfo",

View File

@ -6,3 +6,4 @@
0.06: better contrast for light theme, use fg color instead of dithered for ring 0.06: better contrast for light theme, use fg color instead of dithered for ring
0.07: Use default Bangle formatter for booleans 0.07: Use default Bangle formatter for booleans
0.08: fix idle timer always getting set to true 0.08: fix idle timer always getting set to true
0.09: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -1,4 +1,4 @@
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js"); var SunCalc = require("suncalc"); // from modules folder
const storage = require('Storage'); const storage = require('Storage');
const locale = require("locale"); const locale = require("locale");
const SETTINGS_FILE = "daisy.json"; const SETTINGS_FILE = "daisy.json";
@ -70,7 +70,7 @@ function getSteps() {
try { try {
return Bangle.getHealthStatus("day").steps; return Bangle.getHealthStatus("day").steps;
} catch (e) { } catch (e) {
if (WIDGETS.wpedom !== undefined) if (WIDGETS.wpedom !== undefined)
return WIDGETS.wpedom.getSteps(); return WIDGETS.wpedom.getSteps();
else else
return 0; return 0;
@ -151,7 +151,7 @@ function prevInfo() {
function clearInfo() { function clearInfo() {
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
//g.setColor(g.theme.fg); //g.setColor(g.theme.fg);
g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight); g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight);
} }
function drawInfo() { function drawInfo() {
@ -202,7 +202,7 @@ function drawClock() {
var mm = da[4].substr(3,2); var mm = da[4].substr(3,2);
var steps = getSteps(); var steps = getSteps();
var p_steps = Math.round(100*(steps/10000)); var p_steps = Math.round(100*(steps/10000));
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect(0, 0, w, h); g.fillRect(0, 0, w, h);
@ -218,7 +218,7 @@ function drawClock() {
g.drawString(mm, (w/2) + 1, h/2); g.drawString(mm, (w/2) + 1, h/2);
drawInfo(); drawInfo();
// recalc sunrise / sunset every hour // recalc sunrise / sunset every hour
if (drawCount % 60 == 0) if (drawCount % 60 == 0)
updateSunRiseSunSet(new Date(), location.lat, location.lon); updateSunRiseSunSet(new Date(), location.lat, location.lon);
@ -254,7 +254,7 @@ function resetHrm() {
Bangle.on('HRM', function(hrm) { Bangle.on('HRM', function(hrm) {
hrmCurrent = hrm.bpm; hrmCurrent = hrm.bpm;
hrmConfidence = hrm.confidence; hrmConfidence = hrm.confidence;
log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")"); log_debug("HRM=" + hrm.bpm + " (" + hrm.confidence + ")");
if (infoMode == "ID_HRM" ) drawHrm(); if (infoMode == "ID_HRM" ) drawHrm();
}); });
@ -360,7 +360,7 @@ function getGaugeImage(p) {
palette : pal2, palette : pal2,
buffer : require("heatshrink").decompress(atob("AH4A/AH4AChWq1WpqtUFUgpBFYYABoApggQqDFYlVqBVjFYxZfFQorGLLrWCFZbgbVguoBQcFLD8qFQYMHiosDKzoOJFgZYYKwYPLFgZWawARMLDJWCawgAJcAZWYCZ6FCLCkKFQOgCZ8BFYNUFaZWSLAlAQShWQLAiESQQRtTLAKESFQOoFacFQiSCCwArTgCESQSyEUlTZTboyCnQiSCYQiSCYQiSCZQgdAVxwqYQgSwMVwOoFbMFWBquaWCArBVzKwDbRoqaWATcKbQKuaWAbcKbQKuaWAbcKVzqwNFYIqcWATaKVziwDbhDaebhjaebhgrBbTrcCFZDafbheqFcTcHbT7cDFY0CbT7cDqArxhWqwArfgFVqgrHFUDcBFY0qFcdVFY2oFcMFFY2qFclAFYugFcMBFYsCFctQFYuAFcMAFYsKFctUFYoqigEVFeEqFctVFYmoFccFFYmqFc1AcIdQFccBFf4rbGAoAhKQYr/Fa8FFc9UFYYqkgEVFf4r/FYwDDAEZTDFf4r/Ff4rbqorooArBqArlgIr/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4rbqgrlgorCioroAYIr/Ff4r/FbYDDAEZTDFf4r/FYtAFclVFYUBFc9QFf4rZAgoAgKQor/FbFUFccFFYkVFcwFDioFEAD4lFGIorgPogrtWoYAfqorEgIrlqArFAwgAdEg4rlPgqKFADrUHcQorfA4sVA4wAbEY4zHFbh7GRY4AbaY7jBqAqfERArrMBAAZUxNVbkEVFZAJBFcJhRAC6lJFYLcebQIrIBRTaXJhIrhUhLcfD5YLBbjtVFZTceZ5jceJRpkLVyaiLWDpJNFYKwaUIIrMSIKwaDhw6OVx50NFYKwZDZ6waOaCTBQjBGBZZw8CQi4ZBOR6EYeySEYQSCEaQSITDH6BvGIaKEWQSSEEbqQVVQgRYSKwLGUQgRCQKwTFUC4RYQKwSCTDAhEONQTwULAqcNCARWVLAhGMB55YPDhQqDKy4dFFhAMMLCzgFawZWbEI4AIGogAYFZtAFbgsMFTyyGVkBZOKr7gJazoA/AHIA=")) buffer : require("heatshrink").decompress(atob("AH4A/AH4AChWq1WpqtUFUgpBFYYABoApggQqDFYlVqBVjFYxZfFQorGLLrWCFZbgbVguoBQcFLD8qFQYMHiosDKzoOJFgZYYKwYPLFgZWawARMLDJWCawgAJcAZWYCZ6FCLCkKFQOgCZ8BFYNUFaZWSLAlAQShWQLAiESQQRtTLAKESFQOoFacFQiSCCwArTgCESQSyEUlTZTboyCnQiSCYQiSCYQiSCZQgdAVxwqYQgSwMVwOoFbMFWBquaWCArBVzKwDbRoqaWATcKbQKuaWAbcKbQKuaWAbcKVzqwNFYIqcWATaKVziwDbhDaebhjaebhgrBbTrcCFZDafbheqFcTcHbT7cDFY0CbT7cDqArxhWqwArfgFVqgrHFUDcBFY0qFcdVFY2oFcMFFY2qFclAFYugFcMBFYsCFctQFYuAFcMAFYsKFctUFYoqigEVFeEqFctVFYmoFccFFYmqFc1AcIdQFccBFf4rbGAoAhKQYr/Fa8FFc9UFYYqkgEVFf4r/FYwDDAEZTDFf4r/Ff4rbqorooArBqArlgIr/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4rbqgrlgorCioroAYIr/Ff4r/FbYDDAEZTDFf4r/FYtAFclVFYUBFc9QFf4rZAgoAgKQor/FbFUFccFFYkVFcwFDioFEAD4lFGIorgPogrtWoYAfqorEgIrlqArFAwgAdEg4rlPgqKFADrUHcQorfA4sVA4wAbEY4zHFbh7GRY4AbaY7jBqAqfERArrMBAAZUxNVbkEVFZAJBFcJhRAC6lJFYLcebQIrIBRTaXJhIrhUhLcfD5YLBbjtVFZTceZ5jceJRpkLVyaiLWDpJNFYKwaUIIrMSIKwaDhw6OVx50NFYKwZDZ6waOaCTBQjBGBZZw8CQi4ZBOR6EYeySEYQSCEaQSITDH6BvGIaKEWQSSEEbqQVVQgRYSKwLGUQgRCQKwTFUC4RYQKwSCTDAhEONQTwULAqcNCARWVLAhGMB55YPDhQqDKy4dFFhAMMLCzgFawZWbEI4AIGogAYFZtAFbgsMFTyyGVkBZOKr7gJazoA/AHIA="))
}; };
// p90 // p90
if (p >= 90 && p < 100) return { if (p >= 90 && p < 100) return {
width : 176, height : 176, bpp : 2, width : 176, height : 176, bpp : 2,
@ -410,7 +410,7 @@ function BUTTON(name,x,y,w,h,c,f,tx) {
// if pressed the callback // if pressed the callback
BUTTON.prototype.check = function(x,y) { BUTTON.prototype.check = function(x,y) {
//console.log(this.name + ":check() x=" + x + " y=" + y +"\n"); //console.log(this.name + ":check() x=" + x + " y=" + y +"\n");
if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) { if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) {
log_debug(this.name + ":callback\n"); log_debug(this.name + ":callback\n");
this.callback(); this.callback();
@ -472,7 +472,7 @@ function checkIdle() {
warned = false; warned = false;
return; return;
} }
let hour = (new Date()).getHours(); let hour = (new Date()).getHours();
let active = (hour >= 9 && hour < 21); let active = (hour >= 9 && hour < 21);
//let active = true; //let active = true;
@ -501,7 +501,7 @@ function buzzer(n) {
if (n-- < 1) return; if (n-- < 1) return;
Bangle.buzz(250); Bangle.buzz(250);
if (buzzTimeout) clearTimeout(buzzTimeout); if (buzzTimeout) clearTimeout(buzzTimeout);
buzzTimeout = setTimeout(function() { buzzTimeout = setTimeout(function() {
buzzTimeout = undefined; buzzTimeout = undefined;

View File

@ -1,6 +1,6 @@
{ "id": "daisy", { "id": "daisy",
"name": "Daisy", "name": "Daisy",
"version":"0.08", "version":"0.09",
"dependencies": {"mylocation":"app"}, "dependencies": {"mylocation":"app"},
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times", "description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
"icon": "app.png", "icon": "app.png",

View File

@ -11,7 +11,8 @@
"custom": "custom.html", "custom": "custom.html",
"storage": [ "storage": [
{"name":"espruinoprog.app.js","url":"app.js"}, {"name":"espruinoprog.app.js","url":"app.js"},
{"name":"espruinoprog.img","url":"app-icon.js","evaluate":true}, {"name":"espruinoprog.img","url":"app-icon.js","evaluate":true}
], "data": [
{"name":"espruinoprog.json"} {"name":"espruinoprog.json"}
] ]
} }

View File

@ -11,7 +11,8 @@
"storage": [ "storage": [
{"name":"hourstrike.app.js","url":"app.js"}, {"name":"hourstrike.app.js","url":"app.js"},
{"name":"hourstrike.boot.js","url":"boot.js"}, {"name":"hourstrike.boot.js","url":"boot.js"},
{"name":"hourstrike.img","url":"app-icon.js","evaluate":true}, {"name":"hourstrike.img","url":"app-icon.js","evaluate":true}
], "data" : [
{"name":"hourstrike.json","url":"hourstrike.json"} {"name":"hourstrike.json","url":"hourstrike.json"}
] ]
} }

View File

@ -12,3 +12,4 @@
0.26: BJS2: Swipe down to rotate 180 degree 0.26: BJS2: Swipe down to rotate 180 degree
0.27: BJS2: Changed swipe down to swipe up 0.27: BJS2: Changed swipe down to swipe up
0.28: Reverted changes to implementation of 0.25 0.28: Reverted changes to implementation of 0.25
0.29: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -15,7 +15,7 @@ require("Font5x9Numeric7Seg").add(Graphics);
require("FontTeletext10x18Ascii").add(Graphics); require("FontTeletext10x18Ascii").add(Graphics);
// Font for single secondary time // Font for single secondary time
const secondaryTimeFontSize = 4; const secondaryTimeFontSize = 4;
const secondaryTimeZoneFontSize = 2; const secondaryTimeZoneFontSize = 2;
// Font / columns for multiple secondary times // Font / columns for multiple secondary times
@ -39,14 +39,14 @@ const yposWorld = big ? 170 : 120;
const OFFSET_TIME_ZONE = 0; const OFFSET_TIME_ZONE = 0;
const OFFSET_HOURS = 1; const OFFSET_HOURS = 1;
var PosInterval = 0; var PosInterval = 0;
var offsets = require("Storage").readJSON("hworldclock.settings.json") || []; var offsets = require("Storage").readJSON("hworldclock.settings.json") || [];
//=======Sun //=======Sun
setting = require("Storage").readJSON("setting.json",1); setting = require("Storage").readJSON("setting.json",1);
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
SunCalc = require("hsuncalc.js"); SunCalc = require("suncalc"); // from modules folder
const LOCATION_FILE = "mylocation.json"; const LOCATION_FILE = "mylocation.json";
var rise = "read"; var rise = "read";
var set = "..."; var set = "...";
@ -87,7 +87,7 @@ const mockOffsets = {
//offsets = mockOffsets.fourOffsets; // should render in columns //offsets = mockOffsets.fourOffsets; // should render in columns
// END TESTING CODE // END TESTING CODE
// Load settings // Load settings
function loadMySettings() { function loadMySettings() {
@ -141,11 +141,9 @@ function getCurrentTimeFromOffset(dt, offset) {
function updatePos() { function updatePos() {
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"}; coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
if (coord.lat != 0 && coord.lon != 0) { if (coord.lat != 0 && coord.lon != 0) {
//pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon); times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5); rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
set = "v" + times.sunset.toString().split(" ")[4].substr(0,5); set = "v" + times.sunset.toString().split(" ")[4].substr(0,5);
//noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
} else { } else {
rise = null; rise = null;
set = null; set = null;
@ -179,7 +177,7 @@ function drawSeconds() {
//console.log(seconds); //console.log(seconds);
if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x if (Bangle.isLocked() && secondsMode != "always") seconds = seconds.slice(0, -1) + ':::'; // we use :: as the font does not have an x
//console.log(seconds); //console.log(seconds);
g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true); g.drawString(`${seconds}`, xyCenterSeconds, yposTime+14, true);
queueDrawSeconds(); queueDrawSeconds();
} }
@ -196,18 +194,18 @@ function draw() {
let time = da[4].split(":"); let time = da[4].split(":");
let hours = time[0], let hours = time[0],
minutes = time[1]; minutes = time[1];
if (_12hour){ if (_12hour){
//do 12 hour stuff //do 12 hour stuff
if (hours > 12) { if (hours > 12) {
ampm = "PM"; ampm = "PM";
hours = hours - 12; hours = hours - 12;
if (hours < 10) hours = doublenum(hours); if (hours < 10) hours = doublenum(hours);
} else { } else {
ampm = "AM"; ampm = "AM";
} }
} }
//g.setFont(font, primaryTimeFontSize); //g.setFont(font, primaryTimeFontSize);
g.setFont("5x9Numeric7Seg",primaryTimeFontSize); g.setFont("5x9Numeric7Seg",primaryTimeFontSize);
@ -221,18 +219,18 @@ function draw() {
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
} }
g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true); g.drawString(`${hours}:${minutes}`, xyCenter-10, yposTime, true);
// am / PM ? // am / PM ?
if (_12hour){ if (_12hour){
//do 12 hour stuff //do 12 hour stuff
//let ampm = require("locale").medidian(new Date()); Not working //let ampm = require("locale").medidian(new Date()); Not working
g.setFont("Vector", 17); g.setFont("Vector", 17);
g.drawString(ampm, xyCenterSeconds, yAmPm, true); g.drawString(ampm, xyCenterSeconds, yAmPm, true);
} }
if (secondsMode != "none") drawSeconds(); // To make sure... if (secondsMode != "none") drawSeconds(); // To make sure...
// draw Day, name of month, Date // draw Day, name of month, Date
//DATE //DATE
let localDate = require("locale").date(new Date(), 1); let localDate = require("locale").date(new Date(), 1);
localDate = localDate.substring(0, localDate.length - 5); localDate = localDate.substring(0, localDate.length - 5);
@ -251,7 +249,7 @@ function draw() {
if (offsets.length === 1) { if (offsets.length === 1) {
let date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)]; let date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
// For a single secondary timezone, draw it bigger and drop time zone to second line // For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30; const xOffset = 30;
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true); g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
@ -277,7 +275,7 @@ function draw() {
g.setFontAlign(-1, 0).setFont("Vector",12).drawString(`${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw rise g.setFontAlign(-1, 0).setFont("Vector",12).drawString(`${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw rise
g.setFontAlign(1, 0).drawString(`${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw set g.setFontAlign(1, 0).drawString(`${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw set
} else { } else {
g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true); g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true);
} }
} }
//debug settings //debug settings
@ -287,7 +285,7 @@ function draw() {
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true); //g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
queueDraw(); queueDraw();
if (secondsMode != "none") queueDrawSeconds(); if (secondsMode != "none") queueDrawSeconds();
} }
@ -307,7 +305,7 @@ Bangle.setUI({
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined; drawTimeoutSeconds = undefined;
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
}}); }});
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
@ -328,15 +326,15 @@ if (!Bangle.isLocked()) { // Initial state
if (secondsMode != "none") { if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined; drawTimeoutSeconds = undefined;
} }
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
draw(); // draw immediately, queue redraw draw(); // draw immediately, queue redraw
}else{ }else{
if (secondsMode == "always") secondsTimeout = 1000; if (secondsMode == "always") secondsTimeout = 1000;
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000; if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
if (secondsMode != "none") { if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined; drawTimeoutSeconds = undefined;
@ -350,9 +348,9 @@ if (!Bangle.isLocked()) { // Initial state
updatePos(); updatePos();
} }
draw(); // draw immediately, queue redraw draw(); // draw immediately, queue redraw
} }
Bangle.on('lock',on=>{ Bangle.on('lock',on=>{
if (!on) { // UNlocked if (!on) { // UNlocked
@ -366,7 +364,7 @@ Bangle.on('lock',on=>{
if (secondsMode != "none") { if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined; drawTimeoutSeconds = undefined;
} }
if (drawTimeout) clearTimeout(drawTimeout); if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined; drawTimeout = undefined;
@ -375,7 +373,7 @@ Bangle.on('lock',on=>{
if (secondsMode == "always") secondsTimeout = 1000; if (secondsMode == "always") secondsTimeout = 1000;
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000; if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
if (secondsMode != "none") { if (secondsMode != "none") {
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds); if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined; drawTimeoutSeconds = undefined;
@ -388,7 +386,7 @@ Bangle.on('lock',on=>{
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
updatePos(); updatePos();
} }
draw(); // draw immediately, queue redraw draw(); // draw immediately, queue redraw
} }
}); });
} }

View File

@ -1,298 +0,0 @@
/* Module suncalc.js
(c) 2011-2015, Vladimir Agafonkin
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
https://github.com/mourner/suncalc
PB: Usage:
E.setTimeZone(2); // 1 = MEZ, 2 = MESZ
SunCalc = require("suncalc.js");
pos = SunCalc.getPosition(Date.now(), 53.3, 10.1);
times = SunCalc.getTimes(Date.now(), 53.3, 10.1);
rise = times.sunrise; // Date object
rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm
*/
var exports={};
// shortcuts for easier to read formulas
var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
asin = Math.asin,
atan = Math.atan2,
acos = Math.acos,
rad = PI / 180;
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
// date/time constants and conversions
var dayMs = 1000 * 60 * 60 * 24,
J1970 = 2440588,
J2000 = 2451545;
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021
function toDays(date) { return toJulian(date) - J2000; }
// general calculations for position
var e = rad * 23.4397; // obliquity of the Earth
function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
function astroRefraction(h) {
if (h < 0) // the following formula works for positive altitudes only.
h = 0; // if h = -0.08901179 a div/0 would occur.
// formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
// 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
}
// general sun calculations
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
function eclipticLongitude(M) {
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
P = rad * 102.9372; // perihelion of the Earth
return M + C + P + PI;
}
function sunCoords(d) {
var M = solarMeanAnomaly(d),
L = eclipticLongitude(M);
return {
dec: declination(L, 0),
ra: rightAscension(L, 0)
};
}
// calculates sun position for a given date and latitude/longitude
exports.getPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = sunCoords(d),
H = siderealTime(d, lw) - c.ra;
return {
azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg
altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg
};
};
// sun times configuration (angle, morning name, evening name)
var times = [
[-0.833, 'sunrise', 'sunset' ]
];
// calculations for sun times
var J0 = 0.0009;
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
// returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L) {
var w = hourAngle(h, phi, dec),
a = approxTransit(w, lw, n);
return solarTransitJ(a, M, L);
}
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
exports.getTimes = function (date, lat, lng, height) {
height = height || 0;
var lw = rad * -lng,
phi = rad * lat,
dh = observerAngle(height),
d = toDays(date),
n = julianCycle(d, lw),
ds = approxTransit(0, lw, n),
M = solarMeanAnomaly(ds),
L = eclipticLongitude(M),
dec = declination(L, 0),
Jnoon = solarTransitJ(ds, M, L),
i, len, time, h0, Jset, Jrise;
var result = {
solarNoon: fromJulian(Jnoon),
nadir: fromJulian(Jnoon - 0.5)
};
for (i = 0, len = times.length; i < len; i += 1) {
time = times[i];
h0 = (time[0] + dh) * rad;
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = fromJulian(Jrise);
result[time[2]] = fromJulian(Jset);
}
return result;
};
// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
function moonCoords(d) { // geocentric ecliptic coordinates of the moon
var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
M = rad * (134.963 + 13.064993 * d), // mean anomaly
F = rad * (93.272 + 13.229350 * d), // mean distance
l = L + rad * 6.289 * sin(M), // longitude
b = rad * 5.128 * sin(F), // latitude
dt = 385001 - 20905 * cos(M); // distance to the moon in km
return {
ra: rightAscension(l, b),
dec: declination(l, b),
dist: dt
};
}
getMoonPosition = function (date, lat, lng) {
var lw = rad * -lng,
phi = rad * lat,
d = toDays(date),
c = moonCoords(d),
H = siderealTime(d, lw) - c.ra,
h = altitude(H, phi, c.dec),
// formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
h = h + astroRefraction(h); // altitude correction for refraction
return {
azimuth: azimuth(H, phi, c.dec),
altitude: h,
distance: c.dist,
parallacticAngle: pa
};
};
// calculations for illumination parameters of the moon,
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
getMoonIllumination = function (date) {
var d = toDays(date || new Date()),
s = sunCoords(d),
m = moonCoords(d),
sdist = 149598000, // distance from Earth to Sun in km
phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
return {
fraction: (1 + cos(inc)) / 2,
phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
angle: angle
};
};
function hoursLater(date, h) {
return new Date(date.valueOf() + h * dayMs / 24);
}
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
getMoonTimes = function (date, lat, lng, inUTC) {
var t = new Date(date);
if (inUTC) t.setUTCHours(0, 0, 0, 0);
else t.setHours(0, 0, 0, 0);
var hc = 0.133 * rad,
h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
// go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
for (var i = 1; i <= 24; i += 2) {
h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
a = (h0 + h2) / 2 - h1;
b = (h2 - h0) / 2;
xe = -b / (2 * a);
ye = (a * xe + b) * xe + h1;
d = b * b - 4 * a * h1;
roots = 0;
if (d >= 0) {
dx = Math.sqrt(d) / (Math.abs(a) * 2);
x1 = xe - dx;
x2 = xe + dx;
if (Math.abs(x1) <= 1) roots++;
if (Math.abs(x2) <= 1) roots++;
if (x1 < -1) x1 = x2;
}
if (roots === 1) {
if (h0 < 0) rise = i + x1;
else set = i + x1;
} else if (roots === 2) {
rise = i + (ye < 0 ? x2 : x1);
set = i + (ye < 0 ? x1 : x2);
}
if (rise && set) break;
h0 = h2;
}
var result = {};
if (rise) result.rise = hoursLater(t, rise);
if (set) result.set = hoursLater(t, set);
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
return result;
};

View File

@ -2,7 +2,7 @@
"id": "hworldclock", "id": "hworldclock",
"name": "Hanks World Clock", "name": "Hanks World Clock",
"shortName": "Hanks World Clock", "shortName": "Hanks World Clock",
"version": "0.28", "version": "0.29",
"description": "Current time zone plus up to three others", "description": "Current time zone plus up to three others",
"allow_emulator":true, "allow_emulator":true,
"icon": "app.png", "icon": "app.png",
@ -15,11 +15,10 @@
"storage": [ "storage": [
{"name":"hworldclock.app.js","url":"app.js"}, {"name":"hworldclock.app.js","url":"app.js"},
{"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true}, {"name":"hworldclock.img","url":"hworldclock-icon.js","evaluate":true},
{"name":"hworldclock.settings.js","url":"settings.js"}, {"name":"hworldclock.settings.js","url":"settings.js"}
{"name":"hsuncalc.js","url":"hsuncalc.js"}
], ],
"data": [ "data": [
{"name":"hworldclock.settings.json"}, {"name":"hworldclock.settings.json"},
{"name":"hworldclock.json"} {"name":"hworldclock.json"}
] ]
} }

View File

@ -622,7 +622,7 @@ s.pl = {};
endPerfLog("fullDraw", true); endPerfLog("fullDraw", true);
if (!Bangle.uiRemove){ if (!Bangle.uiRemove){
setUi(); setUi(); // Calls Bangle.setUI() (this comment also fixes lint warning)
let orig = Bangle.drawWidgets; let orig = Bangle.drawWidgets;
Bangle.drawWidgets = ()=>{}; Bangle.drawWidgets = ()=>{};
Bangle.loadWidgets(); Bangle.loadWidgets();

54
apps/lato/README.md Normal file
View File

@ -0,0 +1,54 @@
# Lato
A simple clock with the Lato font, with fast load and clock_info
![](screenshot1.png)
![](screenshot2.png)
![](screenshot3.png)
This clock is a Lato version of Simplest++. Simplest++ provided the
smallest example of a clock that supports 'fast load' and 'clock
info'. Lato takes this one step further and adds the lovely Lato
font. The clock is derived from Simplest++ and inspired by the
Pastel Clock.
## Usage
* When the screen is unlocked, tap at the bottom of the csreen on the information text.
It should change color showing it is selected.
* Swipe up or down to cycle through the info screens that can be displayed
when you have finished tap again towards the centre of the screen to unselect.
* Swipe left or right to change the type of info screens displayed (by default
there is only one type of data so this will have no effect)
* Settings are saved automatically and reloaded along with the clock.
## About Clock Info's
* The clock info modules enable all clocks to add the display of information to the clock face.
* The default clock_info module provides a display of battery %, Steps, Heart Rate and Altitude.
* Installing the [Sunrise ClockInfo](https://banglejs.com/apps/?id=clkinfosunrise) adds Sunrise and Sunset times into the list of info's.
## References
* [What is Fast Load and how does it work](http://www.espruino.com/Bangle.js+Fast+Load)
* [Clock Info Tutorial](http://www.espruino.com/Bangle.js+Clock+Info)
* [How to load modules through the IDE](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
## With Thanks
* Gordon for support
* David Peer for his work on BW Clock
Written by: [Hugh Barney](https://github.com/hughbarney) For support
and discussion please post in the [Bangle JS
Forum](http://forum.espruino.com/microcosms/1424/)

138
apps/lato/app.js Normal file

File diff suppressed because one or more lines are too long

BIN
apps/lato/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

1
apps/lato/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkA/4A/AH4AUiIAKCxXzC5c/C5PyC5cvC8PxC5cfLxNEABhgI+gXNp4X3//+9wAK96PJC6/zC5bvKC6//C5YWKC4nUoMUpoXS8lDn/zmlOC6NCA4ckC6Hkl4HD+QwCC5c+LoIsCoSKBMIPjC5tD//0olEp//mgXNmMRiYuBC4JjBBAYAK+MRj//CwIABBAgXkI5AXOiRyBC4J8BkIXN+dEoKnFiNEAYIXNa4sUC59EJAIACkIHBC5iMCoMTn/zmIuBSQIXODAMRAAKqDABikCAAqqBC8i8CAArCBC/n0C49PC5oA/AH4AIA=="))

16
apps/lato/metadata.json Normal file
View File

@ -0,0 +1,16 @@
{
"id": "lato",
"name": "Lato",
"version": "0.01",
"description": "A Lato Font clock with fast load and clock_info",
"readme": "README.md",
"icon": "app.png",
"screenshots": [{"url":"screenshot3.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"lato.app.js","url":"app.js"},
{"name":"lato.img","url":"icon.js","evaluate":true}
]
}

BIN
apps/lato/screenshot1..png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
apps/lato/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
apps/lato/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -81,3 +81,4 @@
0.57: Fix "unread Timeout" = off (previously defaulted to 60s) 0.57: Fix "unread Timeout" = off (previously defaulted to 60s)
0.58: Fast load messages without writing to flash 0.58: Fast load messages without writing to flash
Don't write messages to flash until the app closes Don't write messages to flash until the app closes
0.59: Ensure we do write messages if messages app can't be fast loaded (see #2373)

View File

@ -18,8 +18,11 @@ exports.listener = function(type, msg) {
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true; if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
else return; else return;
} }
if (Bangle.load === load) { if (Bangle.load === load || !Bangle.uiRemove) {
// no fast loading: store message to flash // no fast loading: store message to flash
/* FIXME: Maybe we need a better way of deciding if an app will
be fast loaded than just hard-coding a Bangle.uiRemove check.
Bangle.load could return a bool (as the load doesn't happen immediately). */
require("messages").save(msg); require("messages").save(msg);
} else { } else {
if (!Bangle.MESSAGES) Bangle.MESSAGES = []; if (!Bangle.MESSAGES) Bangle.MESSAGES = [];

View File

@ -1,7 +1,8 @@
{ {
"id": "messagegui", "id": "messagegui",
"name": "Message UI", "name": "Message UI",
"version": "0.58", "shortName": "Messages",
"version": "0.59",
"description": "Default app to display notifications from iOS and Gadgetbridge/Android", "description": "Default app to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",

View File

@ -19,3 +19,4 @@
0.16: make check_idle boolean setting work properly with new B2 menu 0.16: make check_idle boolean setting work properly with new B2 menu
0.17: Use default Bangle formatter for booleans 0.17: Use default Bangle formatter for booleans
0.18: fix idle option always getting defaulted to true 0.18: fix idle option always getting defaulted to true
0.19: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -2,7 +2,7 @@
"id": "pastel", "id": "pastel",
"name": "Pastel Clock", "name": "Pastel Clock",
"shortName": "Pastel", "shortName": "Pastel",
"version": "0.18", "version": "0.19",
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
"icon": "pastel.png", "icon": "pastel.png",
"dependencies": {"mylocation":"app","weather":"app"}, "dependencies": {"mylocation":"app","weather":"app"},

View File

@ -1,4 +1,4 @@
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js"); var SunCalc = require("suncalc"); // from modules folder
require("f_latosmall").add(Graphics); require("f_latosmall").add(Graphics);
const storage = require('Storage'); const storage = require('Storage');
const locale = require("locale"); const locale = require("locale");
@ -85,7 +85,7 @@ function getSteps() {
try { try {
return Bangle.getHealthStatus("day").steps; return Bangle.getHealthStatus("day").steps;
} catch (e) { } catch (e) {
if (WIDGETS.wpedom !== undefined) if (WIDGETS.wpedom !== undefined)
return WIDGETS.wpedom.getSteps(); return WIDGETS.wpedom.getSteps();
else else
return '???'; return '???';
@ -181,12 +181,12 @@ function drawClock() {
var d = new Date(); var d = new Date();
var da = d.toString().split(" "); var da = d.toString().split(" ");
var time = da[4].substr(0,5); var time = da[4].substr(0,5);
var hh = da[4].substr(0,2); var hh = da[4].substr(0,2);
var mm = da[4].substr(3,2); var mm = da[4].substr(3,2);
var day = da[0]; var day = da[0];
var month_day = da[1] + " " + da[2]; var month_day = da[1] + " " + da[2];
// fix hh for 12hr clock // fix hh for 12hr clock
var h2 = "0" + parseInt(hh) % 12 || 12; var h2 = "0" + parseInt(hh) % 12 || 12;
if (parseInt(hh) > 12) if (parseInt(hh) > 12)
@ -215,12 +215,12 @@ function drawClock() {
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect(Bangle.appRect); g.fillRect(Bangle.appRect);
// draw a grid like graph paper // draw a grid like graph paper
if (settings.grid && process.env.HWVERSION !=1) { if (settings.grid && process.env.HWVERSION !=1) {
g.setColor("#0f0"); g.setColor("#0f0");
for (var gx=20; gx <= w; gx += 20) for (var gx=20; gx <= w; gx += 20)
g.drawLine(gx, 30, gx, h - 24); g.drawLine(gx, 30, gx, h - 24);
for (var gy=30; gy <= h - 24; gy += 20) for (var gy=30; gy <= h - 24; gy += 20)
g.drawLine(0, gy, w, gy); g.drawLine(0, gy, w, gy);
} }
@ -238,7 +238,7 @@ function drawClock() {
g.drawString( (w_wind.split(' ').slice(0, 2).join(' ')), (w/2) + 6, 24 + ((y - 24)/2)); g.drawString( (w_wind.split(' ').slice(0, 2).join(' ')), (w/2) + 6, 24 + ((y - 24)/2));
// display first 2 words of the wind string eg '4 mph' // display first 2 words of the wind string eg '4 mph'
} }
if (settings.font == "Architect") if (settings.font == "Architect")
g.setFontArchitect(); g.setFontArchitect();
else if (settings.font == "GochiHand") else if (settings.font == "GochiHand")
@ -253,7 +253,7 @@ function drawClock() {
g.setFontSpecialElite(); g.setFontSpecialElite();
else else
g.setFontLato(); g.setFontLato();
g.setFontAlign(1,-1); // right aligned g.setFontAlign(1,-1); // right aligned
g.drawString(hh, x - 6, y); g.drawString(hh, x - 6, y);
g.setFontAlign(-1,-1); // left aligned g.setFontAlign(-1,-1); // left aligned
@ -310,7 +310,7 @@ function BUTTON(name,x,y,w,h,c,f,tx) {
// if pressed the callback // if pressed the callback
BUTTON.prototype.check = function(x,y) { BUTTON.prototype.check = function(x,y) {
//console.log(this.name + ":check() x=" + x + " y=" + y +"\n"); //console.log(this.name + ":check() x=" + x + " y=" + y +"\n");
if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) { if (x>= this.x && x<= (this.x + this.w) && y>= this.y && y<= (this.y + this.h)) {
log_debug(this.name + ":callback\n"); log_debug(this.name + ":callback\n");
this.callback(); this.callback();
@ -366,7 +366,7 @@ function checkIdle() {
warned = false; warned = false;
return; return;
} }
let hour = (new Date()).getHours(); let hour = (new Date()).getHours();
let active = (hour >= 9 && hour < 21); let active = (hour >= 9 && hour < 21);
//let active = true; //let active = true;
@ -397,7 +397,7 @@ function buzzer(n) {
if (n-- < 1) return; if (n-- < 1) return;
Bangle.buzz(250); Bangle.buzz(250);
if (buzzTimeout) clearTimeout(buzzTimeout); if (buzzTimeout) clearTimeout(buzzTimeout);
buzzTimeout = setTimeout(function() { buzzTimeout = setTimeout(function() {
buzzTimeout = undefined; buzzTimeout = undefined;

View File

@ -12,7 +12,8 @@
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"presentor.app.js","url":"app.js"}, {"name":"presentor.app.js","url":"app.js"},
{"name":"presentor.img","url":"app-icon.js","evaluate":true}, {"name":"presentor.img","url":"app-icon.js","evaluate":true}
], "data": [
{"name":"presentor.json","url":"settings.json"} {"name":"presentor.json","url":"settings.json"}
] ]
} }

View File

@ -1,3 +1,4 @@
0.01: first release 0.01: first release
0.02: added option to buzz on prime, with settings 0.02: added option to buzz on prime, with settings
0.03: added option to debug settings and test fw 2.15.93 load speed ups 0.03: added option to debug settings and test fw 2.15.93 load speed ups
0.04: changed icon

View File

@ -1,4 +1,4 @@
# Prime Time Lato (clock) # Prime Lato (clock)
A watchface that displays time and its prime factors in the Lato font. A watchface that displays time and its prime factors in the Lato font.
For example when the time is 21:05, the prime factors are 5,421. For example when the time is 21:05, the prime factors are 5,421.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV")) require("heatshrink").decompress(atob("mEw4X/AAIHB8cYttrJf4AR1gKJgdYBZMCBZdcBZMNsALKuALJhNABZMFwALJmvAAwkOqvAmtAkwSF83+uEV4EMOIpZBznWII5NB7mXGo5BB7Z0HkpBB6x0HFYXVNA4rC6pcFAANXDQhSFqgaEBZGYaBLfIaAUBBZUJNQ4jCm+cHZPcBZFXgdwzELBg1W/PAy/rBY3VPAOVTY863kAnaPHAH4A/ADAA=="))

View File

@ -1,6 +1,6 @@
{ "id": "primetimelato", { "id": "primetimelato",
"name": "Prime Time Lato", "name": "Prime Lato",
"version": "0.03", "version": "0.04",
"type": "clock", "type": "clock",
"description": "A clock that tells you the primes of the time in the Lato font", "description": "A clock that tells you the primes of the time in the Lato font",
"icon": "app.png", "icon": "app.png",

View File

@ -9,7 +9,8 @@
0.09: fix battery icon size 0.09: fix battery icon size
0.10: Tell clock widgets to hide. 0.10: Tell clock widgets to hide.
0.11: fix issue https://github.com/espruino/BangleApps/issues/2128 (#2128) ( settings undefined ) 0.11: fix issue https://github.com/espruino/BangleApps/issues/2128 (#2128) ( settings undefined )
0.12: implemented widget_utils 0.12: implemented widget_utils
0.13: convert var/function into let 0.13: convert var/function into let
0.14: cleanup code and fix fastload issue 0.14: cleanup code and fix fastload issue
0.15: fix draw before widget hide 0.15: fix draw before widget hide
0.16: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -2,7 +2,7 @@
"id": "rebble", "id": "rebble",
"name": "Rebble Clock", "name": "Rebble Clock",
"shortName": "Rebble", "shortName": "Rebble",
"version": "0.15", "version": "0.16",
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
"readme": "README.md", "readme": "README.md",
"icon": "rebble.png", "icon": "rebble.png",
@ -14,7 +14,6 @@
"storage": [ "storage": [
{"name":"rebble.app.js","url":"rebble.app.js"}, {"name":"rebble.app.js","url":"rebble.app.js"},
{"name":"rebble.settings.js","url":"rebble.settings.js"}, {"name":"rebble.settings.js","url":"rebble.settings.js"},
{"name":"rebble.img","url":"rebble.icon.js","evaluate":true}, {"name":"rebble.img","url":"rebble.icon.js","evaluate":true}
{"name":"suncalc","url":"suncalc.js"}
] ]
} }

View File

@ -11,7 +11,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
{ {
let SunCalc = require("suncalc"); let SunCalc = require("suncalc"); // from modules folder
const SETTINGS_FILE = "rebble.json"; const SETTINGS_FILE = "rebble.json";
const LOCATION_FILE = "mylocation.json"; const LOCATION_FILE = "mylocation.json";
const GLOBAL_SETTINGS = "setting.json"; const GLOBAL_SETTINGS = "setting.json";
@ -49,7 +49,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
} }
if(settings.sideTap!=0) if(settings.sideTap!=0)
sideBar=parseInt(settings.sideTap)-1; //tab to sideBar=parseInt(settings.sideTap)-1; //tab to
is12Hour = (require("Storage").readJSON(GLOBAL_SETTINGS, 1) || {})["12hour"] || false; is12Hour = (require("Storage").readJSON(GLOBAL_SETTINGS, 1) || {})["12hour"] || false;
} }
@ -110,15 +110,15 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
let date = new Date(); let date = new Date();
let hh = date.getHours(); let hh = date.getHours();
let mm = date.getMinutes(); let mm = date.getMinutes();
hh = formatHours(hh); hh = formatHours(hh);
mm = zeroPad(mm,2); mm = zeroPad(mm,2);
//const t = 6; //const t = 6;
if (drawCount % 60 == 0) if (drawCount % 60 == 0)
updateSunRiseSunSet(location.lat, location.lon); updateSunRiseSunSet(location.lat, location.lon);
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect(0, 0, w2, h); g.fillRect(0, 0, w2, h);
@ -143,7 +143,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
drawSideBar3(); drawSideBar3();
break; break;
} }
drawCount++; drawCount++;
queueDraw(); queueDraw();
} }
@ -154,14 +154,14 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
let dd=date.getDate(); let dd=date.getDate();
let mm=require("date_utils").month(date.getMonth()+1,1).toUpperCase(); let mm=require("date_utils").month(date.getMonth()+1,1).toUpperCase();
drawBattery(w2 + (w-w2-wb)/2, h/10, wb, 17); drawBattery(w2 + (w-w2-wb)/2, h/10, wb, 17);
setTextColor(); setTextColor();
g.setFont('Vector', 20); g.setFont('Vector', 20);
g.setFontAlign(0, -1); g.setFontAlign(0, -1);
g.drawString(E.getBattery() + '%', w3, (h/10) + 17 + 7); g.drawString(E.getBattery() + '%', w3, (h/10) + 17 + 7);
drawDateAndCalendar(w3, h/2, dy, dd, mm); drawDateAndCalendar(w3, h/2, dy, dd, mm);
} }
@ -250,7 +250,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
} }
} }
// format steps so they fit in the place // format steps so they fit in the place
let formatSteps=function() { let formatSteps=function() {
let s = Bangle.getHealthStatus("day").steps; let s = Bangle.getHealthStatus("day").steps;
@ -292,8 +292,8 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
let chargingListener= function(charging) { let chargingListener= function(charging) {
//redraw the sidebar ( with the battery ) //redraw the sidebar ( with the battery )
switch(sideBar) { switch(sideBar) {
case 0: case 0:
@ -304,7 +304,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
break; break;
} }
} }
let deleteAll=function() let deleteAll=function()
{ {
// Called to unload all of the clock app // Called to unload all of the clock app
@ -320,7 +320,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
log_debug("starting.."); log_debug("starting..");
loadSettings(); loadSettings();
loadLocation(); loadLocation();
if(settings.autoCycle || settings.sideTap==0) if(settings.autoCycle || settings.sideTap==0)
{ {
Bangle.setUI({ Bangle.setUI({
@ -332,7 +332,7 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
if (btn>0) nextSidebar(); if (btn>0) nextSidebar();
draw(); draw();
}); });
} }
else{ else{
Bangle.setUI({ Bangle.setUI({
@ -354,4 +354,4 @@ Graphics.prototype.setFontKdamThmor = function(scale) {
main(); main();
} }

View File

@ -1,143 +0,0 @@
/*
(c) 2011-2015, Vladimir Agafonkin
SunCalc is a JavaScript library for calculating sun/moon position and light phases.
https://github.com/mourner/suncalc
edit for banglejs
*/
(function () { 'use strict';
// shortcuts for easier to read formulas
var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
tan = Math.tan,
asin = Math.asin,
atan = Math.atan2,
acos = Math.acos,
rad = PI / 180;
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
// date/time constants and conversions
var dayMs = 1000 * 60 * 60 * 24,
J1970 = 2440588,
J2000 = 2451545;
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); }
function toDays(date) { return toJulian(date) - J2000; }
// general calculations for position
var e = rad * 23.4397; // obliquity of the Earth
function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
// general sun calculations
function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
function eclipticLongitude(M) {
var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
P = rad * 102.9372; // perihelion of the Earth
return M + C + P + PI;
}
var SunCalc = {};
// sun times configuration (angle, morning name, evening name)
var times = SunCalc.times = [
[-0.833, 'sunrise', 'sunset' ],
[ -0.3, 'sunriseEnd', 'sunsetStart' ],
[ -6, 'dawn', 'dusk' ],
[ -12, 'nauticalDawn', 'nauticalDusk'],
[ -18, 'nightEnd', 'night' ],
[ 6, 'goldenHourEnd', 'goldenHour' ]
];
// calculations for sun times
var J0 = 0.0009;
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
// returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L) {
var w = hourAngle(h, phi, dec),
a = approxTransit(w, lw, n);
return solarTransitJ(a, M, L);
}
// calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon
SunCalc.getTimes = function (date, lat, lng, height) {
height = height || 0;
var lw = rad * -lng,
phi = rad * lat,
dh = observerAngle(height),
d = toDays(date),
n = julianCycle(d, lw),
ds = approxTransit(0, lw, n),
M = solarMeanAnomaly(ds),
L = eclipticLongitude(M),
dec = declination(L, 0),
Jnoon = solarTransitJ(ds, M, L),
i, len, time, h0, Jset, Jrise;
var result = {
solarNoon: fromJulian(Jnoon),
nadir: fromJulian(Jnoon - 0.5)
};
for (i = 0, len = times.length; i < len; i += 1) {
time = times[i];
h0 = (time[0] + dh) * rad;
Jset = getSetJ(h0, lw, phi, dec, n, M, L);
Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = fromJulian(Jrise);
result[time[2]] = fromJulian(Jset);
}
return result;
};
// export as Node module / AMD module / browser variable
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc;
else if (typeof define === 'function' && define.amd) define(SunCalc);
else window.SunCalc = SunCalc;
}());

54
apps/simplestpp/README.md Normal file
View File

@ -0,0 +1,54 @@
# Simplest++ Clock
The simplest working clock, with fast load and clock_info
![](screenshot1.png)
![](screenshot2.png)
![](screenshot3.png)
Simplest++ has 1 clock info menu that is displayed as a single line at the bottom of the screen.
This provides a working demo of how to use the clock_info modules.
## Usage
* When the screen is unlocked, tap at the bottom of the csreen on the information text.
It should change color showing it is selected.
* Swipe up or down to cycle through the info screens that can be displayed
when you have finished tap again towards the centre of the screen to unselect.
* Swipe left or right to change the type of info screens displayed (by default
there is only one type of data so this will have no effect)
* Settings are saved automatically and reloaded along with the clock.
## About Clock Info's
* The clock info modules enable all clocks to add the display of information to the clock face.
* The default clock_info module provides a display of battery %, Steps, Heart Rate and Altitude.
* Installing the [Sunrise ClockInfo](https://banglejs.com/apps/?id=clkinfosunrise) adds Sunrise and Sunset times into the list of info's.
## References
* [What is Fast Load and how does it work](http://www.espruino.com/Bangle.js+Fast+Load)
* [Clock Info Tutorial](http://www.espruino.com/Bangle.js+Clock+Info)
* [How to load modules through the IDE](https://github.com/espruino/BangleApps/blob/master/modules/README.md)
## With Thanks
* Gordon for support
* David Peer for his work on BW Clock
Written by: [Hugh Barney](https://github.com/hughbarney) For support
and discussion please post in the [Bangle JS
Forum](http://forum.espruino.com/microcosms/1424/)

108
apps/simplestpp/app.js Normal file
View File

@ -0,0 +1,108 @@
/**
*
* Simplestpp Clock
*
* The entire clock code is contained within the block below this
* supports 'fast load'
*
* To add support for clock_info_supprt we add the code marked at [1] and [2]
*
*/
{
// must be inside our own scope here so that when we are unloaded everything disappears
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
let draw = function() {
var date = new Date();
var timeStr = require("locale").time(date,1);
var h = g.getHeight();
var w = g.getWidth();
g.reset();
g.setColor(g.theme.bg);
g.fillRect(Bangle.appRect);
g.setFont('Vector', w/3);
g.setFontAlign(0, 0);
g.setColor(g.theme.fg);
g.drawString(timeStr, w/2, h/2);
clockInfoMenu.redraw(); // clock_info_support
// schedule a draw for the next minute
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
};
/**
* clock_info_support
* this is the callback function that get invoked by clockInfoMenu.redraw();
*
* We will display the image and text on the same line and centre the combined
* length of the image+text
*
*
*/
let clockInfoDraw = (itm, info, options) => {
g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg);
//use info.text.toString(), steps does not have length defined
var text_w = g.stringWidth(info.text.toString());
// gap between image and text
var gap = 10;
// width of the image and text combined
var w = gap + (info.img ? 24 :0) + text_w;
// different fg color if we tapped on the menu
if (options.focus) g.setColor(options.hl);
// clear the whole info line
g.clearRect(0, options.y -1, g.getWidth(), options.y+24);
// draw the image if we have one
if (info.img) {
// image start
var x = (g.getWidth() / 2) - (w/2);
g.drawImage(info.img, x, options.y);
// draw the text to the side of the image (left/centre alignment)
g.setFontAlign(-1,0).drawString(info.text, x + 23 + gap, options.y+12);
} else {
// text only option, not tested yet
g.setFontAlign(0,0).drawString(info.text, g.getWidth() / 2, options.y+12);
}
};
// clock_info_support
// retrieve all the clock_info modules that are installed
let clockInfoItems = require("clock_info").load();
// clock_info_support
// setup the way we wish to interact with the menu
// the hl property defines the color the of the info when the menu is selected after tapping on it
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} );
// timeout used to update every minute
var drawTimeout;
g.clear();
// Show launcher when middle button pressed, add updown button handlers
Bangle.setUI({
mode : "clock",
remove : function() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
// remove info menu
clockInfoMenu.remove();
delete clockInfoMenu;
}
});
// Load widgets
Bangle.loadWidgets();
draw();
setTimeout(Bangle.drawWidgets,0);
} // end of clock

BIN
apps/simplestpp/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

1
apps/simplestpp/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4X/AAIHBqOM997IGkq1AKIltVqt4BQ0DBQIAB4ALFktVv/9qtYBYvVA4Ulq4KEgNVwAEBgVVoALDgtcAoc1qAFDitgAocJqguEGoowDgVWBYuVGoUBFwgwCHgUNGgUNuBCCAYY6CBYcJCYUlDYYLCgJxCmozCBYcCG4UVG4QLDgBgCBZeXBY/WBYcC1WtvWqGoILEVAIACJoILQgf/+tf/7jBBYg7JL66DLTZazLZZbjLfZcA6oLFq4EDio8CHQReCGgQwEmpCCHgVVFIUCVAQAD6plCkouEA4VVv/9qoPCAAcDZYa/BAAstBQN4BQwABlWoBRAAr"))

View File

@ -0,0 +1,16 @@
{
"id": "simplestpp",
"name": "Simplest++ Clock",
"version": "0.01",
"description": "The simplest working clock, with fast load and clock_info, acts as a tutorial piece",
"readme": "README.md",
"icon": "app.png",
"screenshots": [{"url":"screenshot3.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"simplestpp.app.js","url":"app.js"},
{"name":"simplestpp.img","url":"icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

2
apps/sunclock/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: First commit
0.02: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

View File

@ -9,8 +9,8 @@ SunCalc = require("suncalc.js");
loc = require('locale'); loc = require('locale');
const LOCATION_FILE = "mylocation.json"; const LOCATION_FILE = "mylocation.json";
const xyCenter = g.getWidth() / 2 + 3; const xyCenter = g.getWidth() / 2 + 3;
const yposTime = 60; const yposTime = 60;
const yposDate = 100; const yposDate = 100;
const yposRS = 135; const yposRS = 135;
const yposPos = 160; const yposPos = 160;
var rise = "07:00"; var rise = "07:00";
@ -19,13 +19,21 @@ var pos = {altitude: 20, azimuth: 135};
var noonpos = {altitude: 37, azimuth: 180}; var noonpos = {altitude: 37, azimuth: 180};
let idTimeout = null; let idTimeout = null;
function updatePos() { function updatePos() {
function radToDeg(pos) {
return { // instead of mofidying suncalc
azimuth: Math.round((pos.azimuth / rad + 180) % 360),
altitude: Math.round( pos.altitude / rad)
};
}
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"}; coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"};
pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon); pos = radToDeg(SunCalc.getPosition(Date.now(), coord.lat, coord.lon));
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon); times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
rise = times.sunrise.toString().split(" ")[4].substr(0,5); rise = times.sunrise.toString().split(" ")[4].substr(0,5);
set = times.sunset.toString().split(" ")[4].substr(0,5); set = times.sunset.toString().split(" ")[4].substr(0,5);
noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon); noonpos = radToDeg(SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon));
} }
function drawSimpleClock() { function drawSimpleClock() {
@ -38,7 +46,7 @@ function drawSimpleClock() {
var time = da[4].substr(0, 5); // draw time var time = da[4].substr(0, 5); // draw time
g.setFont("Vector",60); g.setFont("Vector",60);
g.drawString(time, xyCenter, yposTime, true); g.drawString(time, xyCenter, yposTime, true);
var date = [loc.dow(new Date(),1), loc.date(d,1)].join(" "); // draw day of week, date var date = [loc.dow(new Date(),1), loc.date(d,1)].join(" "); // draw day of week, date
@ -51,7 +59,7 @@ function drawSimpleClock() {
g.setFont("Vector",21); g.setFont("Vector",21);
g.drawString(`H${pos.altitude}/${noonpos.altitude} Az${pos.azimuth}`, xyCenter, yposPos, true); // draw sun pos g.drawString(`H${pos.altitude}/${noonpos.altitude} Az${pos.azimuth}`, xyCenter, yposPos, true); // draw sun pos
let t = d.getSeconds()*1000 + d.getMilliseconds(); let t = d.getSeconds()*1000 + d.getMilliseconds();
idTimeout = setTimeout(drawSimpleClock, 60000 - t); // time till next minute idTimeout = setTimeout(drawSimpleClock, 60000 - t); // time till next minute
} }

View File

@ -1,7 +1,7 @@
{ {
"id": "sunclock", "id": "sunclock",
"name": "Sun Clock", "name": "Sun Clock",
"version": "0.01", "version": "0.02",
"description": "A clock with sunset/sunrise, sun height/azimuth", "description": "A clock with sunset/sunrise, sun height/azimuth",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
@ -11,7 +11,6 @@
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"sunclock.app.js","url":"app.js"}, {"name":"sunclock.app.js","url":"app.js"},
{"name":"sunclock.img","url":"app-icon.js","evaluate":true}, {"name":"sunclock.img","url":"app-icon.js","evaluate":true}
{"name":"suncalc.js","url":"suncalc.js"}
] ]
} }

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Add sunrise/sunset. Fix timer bugs. 0.02: Add sunrise/sunset. Fix timer bugs.
0.03: Use default Bangle formatter for booleans 0.03: Use default Bangle formatter for booleans
0.04: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
{ {
"id": "timerclk", "id": "timerclk",
"name": "Timer Clock", "name": "Timer Clock",
"shortName":"Timer Clock", "shortName":"Timer Clock",
"version":"0.03", "version":"0.04",
"description": "A clock with stopwatches, timers and alarms build in.", "description": "A clock with stopwatches, timers and alarms build in.",
"icon": "app-icon.png", "icon": "app-icon.png",
"type": "clock", "type": "clock",

View File

@ -19,4 +19,3 @@
0.20: Added weather condition with temperature to clkinfo. 0.20: Added weather condition with temperature to clkinfo.
0.21: Updated clkinfo icon. 0.21: Updated clkinfo icon.
0.22: Automatic translation of strings, some left untranslated. 0.22: Automatic translation of strings, some left untranslated.
>>>>>>> b37fcacd1 (weather - autotranslate strings)

1
bin/sanitycheck.js Executable file → Normal file
View File

@ -93,6 +93,7 @@ const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD
/* These are warnings we know about but don't want in our output */ /* These are warnings we know about but don't want in our output */
var KNOWN_WARNINGS = [ var KNOWN_WARNINGS = [
"App gpsrec data file wildcard .gpsrc? does not include app ID", "App gpsrec data file wildcard .gpsrc? does not include app ID",
"App owmweather data file weather.json is also listed as data file for app weather",
]; ];
function globToRegex(pattern) { function globToRegex(pattern) {

View File

@ -1,17 +1,34 @@
/* Module suncalc.js /*
(c) 2011-2015, Vladimir Agafonkin (c) 2011-2015, Vladimir Agafonkin
SunCalc is a JavaScript library for calculating sun/moon position and light phases. SunCalc is a JavaScript library for calculating sun/moon position and light phases.
https://github.com/mourner/suncalc https://github.com/mourner/suncalc
PB: Usage: Copyright (c) 2014, Vladimir Agafonkin
E.setTimeZone(2); // 1 = MEZ, 2 = MESZ All rights reserved.
SunCalc = require("suncalc.js");
pos = SunCalc.getPosition(Date.now(), 53.3, 10.1); Redistribution and use in source and binary forms, with or without modification, are
times = SunCalc.getTimes(Date.now(), 53.3, 10.1); permitted provided that the following conditions are met:
rise = times.sunrise; // Date object
rise_str = rise.getHours() + ':' + rise.getMinutes(); //hh:mm 1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
var exports={};
(function () { 'use strict';
// shortcuts for easier to read formulas // shortcuts for easier to read formulas
@ -26,6 +43,7 @@ var PI = Math.PI,
// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
// date/time constants and conversions // date/time constants and conversions
var dayMs = 1000 * 60 * 60 * 24, var dayMs = 1000 * 60 * 60 * 24,
@ -33,7 +51,7 @@ var dayMs = 1000 * 60 * 60 * 24,
J2000 = 2451545; J2000 = 2451545;
function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } // PB: onece removed + 0.5; included it again 4 Jan 2021 function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); }
function toDays(date) { return toJulian(date) - J2000; } function toDays(date) { return toJulian(date) - J2000; }
@ -81,9 +99,13 @@ function sunCoords(d) {
}; };
} }
var SunCalc = {};
// calculates sun position for a given date and latitude/longitude // calculates sun position for a given date and latitude/longitude
exports.getPosition = function (date, lat, lng) { SunCalc.getPosition = function (date, lat, lng) {
var lw = rad * -lng, var lw = rad * -lng,
phi = rad * lat, phi = rad * lat,
@ -93,19 +115,32 @@ exports.getPosition = function (date, lat, lng) {
H = siderealTime(d, lw) - c.ra; H = siderealTime(d, lw) - c.ra;
return { return {
azimuth: Math.round((azimuth(H, phi, c.dec) / rad + 180) % 360), // PB: converted to deg azimuth: azimuth(H, phi, c.dec),
altitude: Math.round( altitude(H, phi, c.dec) / rad) // PB: converted to deg altitude: altitude(H, phi, c.dec)
}; };
}; };
// sun times configuration (angle, morning name, evening name) // sun times configuration (angle, morning name, evening name)
var times = [ var times = SunCalc.times = [
[-0.833, 'sunrise', 'sunset' ] [-0.833, 'sunrise', 'sunset' ],
[ -0.3, 'sunriseEnd', 'sunsetStart' ],
[ -6, 'dawn', 'dusk' ],
[ -12, 'nauticalDawn', 'nauticalDusk'],
[ -18, 'nightEnd', 'night' ],
[ 6, 'goldenHourEnd', 'goldenHour' ]
]; ];
// adds a custom time to the times config
SunCalc.addTime = function (angle, riseName, setName) {
times.push([angle, riseName, setName]);
};
// calculations for sun times // calculations for sun times
var J0 = 0.0009; var J0 = 0.0009;
function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); } function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
@ -128,7 +163,7 @@ function getSetJ(h, lw, phi, dec, n, M, L) {
// calculates sun times for a given date, latitude/longitude, and, optionally, // calculates sun times for a given date, latitude/longitude, and, optionally,
// the observer height (in meters) relative to the horizon // the observer height (in meters) relative to the horizon
exports.getTimes = function (date, lat, lng, height) { SunCalc.getTimes = function (date, lat, lng, height) {
height = height || 0; height = height || 0;
@ -189,7 +224,7 @@ function moonCoords(d) { // geocentric ecliptic coordinates of the moon
}; };
} }
getMoonPosition = function (date, lat, lng) { SunCalc.getMoonPosition = function (date, lat, lng) {
var lw = rad * -lng, var lw = rad * -lng,
phi = rad * lat, phi = rad * lat,
@ -216,7 +251,7 @@ getMoonPosition = function (date, lat, lng) {
// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
getMoonIllumination = function (date) { SunCalc.getMoonIllumination = function (date) {
var d = toDays(date || new Date()), var d = toDays(date || new Date()),
s = sunCoords(d), s = sunCoords(d),
@ -243,7 +278,7 @@ function hoursLater(date, h) {
// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article // calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
getMoonTimes = function (date, lat, lng, inUTC) { SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
var t = new Date(date); var t = new Date(date);
if (inUTC) t.setUTCHours(0, 0, 0, 0); if (inUTC) t.setUTCHours(0, 0, 0, 0);
else t.setHours(0, 0, 0, 0); else t.setHours(0, 0, 0, 0);
@ -295,4 +330,12 @@ getMoonTimes = function (date, lat, lng, inUTC) {
if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
return result; return result;
}; };
// export as Node module / AMD module / browser variable
if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc;
else if (typeof define === 'function' && define.amd) define(SunCalc);
else window.SunCalc = SunCalc;
}());