Skip to content

Commit db6de49

Browse files
committed
1 parent f04b1d4 commit db6de49

File tree

13 files changed

+240
-0
lines changed

13 files changed

+240
-0
lines changed

apps/tidetimes/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.01: New App!

apps/tidetimes/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Tide Times
2+
3+
A simple app to keep track of tide times. Set up the time of the next high/low tide, and this will show you the next high/low tides based on a 6hr 12min interval.
4+
5+
First discussed [on the forum](https://github.com/orgs/espruino/discussions/7854#discussioncomment-14431487)
6+
7+
## Usage
8+
9+
Run the app, and see a tide graph:
10+
11+
![](screenshot1.png)
12+
13+
Press the button to bring up a menu, where the estimated next low/high tides are.
14+
15+
Choose `Next High/Low` to update the value to what you know is the next high or low tide, tap on the time to confirm. The Tide view will then update.
16+
17+
**Note:** Any time given more than one hour in the past will be considered to be the time of the tide *on the next day*.
18+
19+
## ClockInfo
20+
21+
There is also a Clock Info, so if your clock supports them you show the next high/low tide plus
22+
where you are on the range of the tide. Tapping on the clockinfo takes you to the tides app
23+
for more detailed info.
24+
25+
![](screenshot2.png) ![](screenshot3.png)
26+
27+
## To Do
28+
29+
* Tapping on the high/low tide clockinfo could toggle between next high/low tide?
30+
* We could get the data from an online source with an `interface.html` that could be run from the app loader. For instance:
31+
32+
```
33+
// high/low
34+
https://api.tidesandcurrents.noaa.gov/api/prod/datagetter?product=predictions&begin_date=20251001&end_date=20251003&datum=MLLW&station=8443970&units=english&time_zone=lst_ldt&interval=hilo&format=json&application=my_tide_app
35+
// actual times per hour
36+
https://api.tidesandcurrents.noaa.gov/api/prod/datagetter?product=predictions&begin_date=20251001&end_date=20251003&datum=MLLW&station=8443970&units=english&time_zone=lst_ldt&interval=h&format=json&application=my_tide_app
37+
// station IDs from:
38+
https://tidesandcurrents.noaa.gov/map/
39+
```
40+
41+
But this is USA-only. Admiralty (UK) and Stormglass (worldwide but rate limited) could also be options
42+
43+
## Creator
44+
45+
Gordon Williams

apps/tidetimes/app-icon.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/tidetimes/app.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
const tide = require("tidetimes");
2+
3+
function msToDayHours(ms) {
4+
var d = new Date(ms);
5+
return d.getHours()+d.getMinutes()/60;
6+
}
7+
function dayHoursToStr(t) {
8+
return Math.floor(t) + ":" + Math.round((t-Math.floor(t))*60).toString().padStart(2,0);
9+
}
10+
function getDayStart() {
11+
return new Date((new Date()).toISOString().substr(0,10)).getTime();
12+
}
13+
14+
// redraw the screen
15+
function draw() {
16+
let R = Bangle.appRect;
17+
let dayStart = getDayStart(), // midnight yesterday
18+
dayEnd = dayStart+86400000;
19+
g.reset().clearRect(R);
20+
let y = R.y + 20, h = 50;
21+
g.reset();
22+
function timeToX(time) { return (time-dayStart) * R.w / 86400000; }
23+
function heightToY(v) { return y+(1-v)*h; }
24+
// background fill
25+
g.setColor(g.blendColor(g.theme.bg,"#00f",0.5));
26+
for (let t=dayStart;t<=dayEnd;t+=3600000) {
27+
g.fillRect(timeToX(t-1800000),heightToY(tide.getLevelAt(t)),
28+
timeToX(t+1800000),y+h);
29+
}
30+
// grid lines
31+
g.setColor(g.blendColor(g.theme.bg,g.theme.fg,0.5)).drawLine(0,y+h/2,R.w,y+h/2);
32+
g.setColor(g.theme.fg).drawLine(0,y,R.w,y).drawLine(0,y+h,R.w,y+h);
33+
let x = timeToX(Date.now());
34+
g.fillRect(x-1,y,x+1,y+h);
35+
// mark times
36+
g.setColor(g.theme.fg).setFont("14").setFontAlign(0,0);
37+
let td = tide.getNext(dayStart);
38+
while (td.t<dayEnd) {
39+
g.drawString(require("locale").time(new Date(td.t),1),
40+
timeToX(td.t), (td.v>0.5)?y-8:y+h+12);
41+
td = tide.getNext(td.t+3600000);
42+
}
43+
// Tide text
44+
y+=h+20;
45+
g.setFont("17");
46+
td = tide.getNext(Date.now());
47+
for (let c=0;c<2;c++) {
48+
g.drawString(`Next ${td.v>0.5?"High":"Low"}: `+require("locale").time(new Date(td.t),1),
49+
R.w/2, y+=20);
50+
td = tide.getNext(td.t+3600000);
51+
}
52+
}
53+
54+
function showTides() {
55+
Bangle.setUI({mode: "custom", btn: showMenu});
56+
draw();
57+
}
58+
59+
function showMenu() {
60+
let step = 5/60;
61+
E.showMenu({
62+
"":{title:"Tides", back : showTides },
63+
"Low Tide" : {
64+
value : Math.round(msToDayHours(tide.getNext(Date.now(), false).t)/step)*step,
65+
format : dayHoursToStr,
66+
min : 0, max: 24, step : step, wrap : true,
67+
onchange : v => {
68+
let dayStart = getDayStart();
69+
let currHr = msToDayHours(Date.now());
70+
if (v+1<currHr) v+=24;
71+
tide.offset = dayStart + (v + tide.period/2)*3600000;
72+
tide.save();
73+
showTides();
74+
}
75+
},
76+
"High Tide" : {
77+
value : Math.round(msToDayHours(tide.getNext(Date.now(), true).t)/step)*step,
78+
format : dayHoursToStr,
79+
min : 0, max: 24, step : step, wrap : true,
80+
onchange : v => {
81+
let dayStart = getDayStart();
82+
let currHr = msToDayHours(Date.now());
83+
if (v+1<currHr) v+=24;
84+
tide.offset = dayStart + v*3600000;
85+
tide.save();
86+
showTides();
87+
}
88+
},
89+
"Exit": () => load(),
90+
});
91+
}
92+
93+
Bangle.loadWidgets();
94+
showTides();
95+
Bangle.drawWidgets();
96+

apps/tidetimes/app.png

5.65 KB
Loading

apps/tidetimes/clkinfo.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
(function() {
2+
let interval;
3+
return {
4+
name: "Bangle",
5+
items: [
6+
{ name : "Tide",
7+
get : function() {
8+
let tide = require("tidetimes").getNext(Date.now());
9+
return {
10+
text : require("locale").time(new Date(tide.t),1),
11+
img : (tide.v > 0.5) ?
12+
atob("GBiBAAAAAAAAAAYGBg8PDhmZmHDw8GBgYAAAAAAQAAA4AAB8AAD+AAH/AAP/gAA4AAA4AAA4AAA4AAA4AAA4AAA4AAAAAAAAAAAAAA==") :
13+
atob("GBiBAAAAAAAAAAAAAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAH/wAD/gAB/AAA+AAAcAAAIAAAAAAYGBg8PDhmZmHDw8GBgYAAAAAAAAA==")
14+
};
15+
},
16+
show : function() {
17+
interval = setInterval(() => { this.emit("redraw"); }, 600000); // 10 minutes
18+
},
19+
hide : function() { clearInterval(interval); interval = undefined; },
20+
run : function() { load("tidetimes.app.js"); }
21+
},
22+
{ name : "Tide Height",
23+
get : function() {
24+
let t = Date.now(), scale = 1600000;// ms per pixel
25+
let tides = require("tidetimes");
26+
let b = Graphics.createArrayBuffer(24,24,1);
27+
b.transparent = 0;
28+
for (let x=0;x<24;x+=2) {
29+
b.fillRect(x, 16-tides.getLevelAt(t-scale*(x-12))*12, x+1,17);
30+
}
31+
b.fillPoly([12,17, 9,23, 15,23])
32+
return {
33+
text : Math.round(tides.getLevelAt(t)*100)+"%",
34+
img : b.asImage("string")
35+
};
36+
},
37+
show : function() {
38+
interval = setInterval(() => { this.emit("redraw"); }, 600000); // 10 minutes
39+
},
40+
hide : function() { clearInterval(interval); interval = undefined; },
41+
run : function() { load("tidetimes.app.js"); }
42+
}
43+
]
44+
};
45+
}) // must not have a semi-colon!

apps/tidetimes/icon_tidedown.png

607 Bytes
Loading

apps/tidetimes/icon_tideup.png

596 Bytes
Loading

apps/tidetimes/lib.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
exports = {
2+
period : 12*3600000 + 25*60000, // 12h25, in msec
3+
offset : 0, // time(in ms) since 1970 of a high tide
4+
/// Given a unix timestamp, work out the tide level (from 0 to 1)
5+
getLevelAt : function(time) {
6+
return 0.5 + 0.5*Math.cos((time-this.offset) * 2 * Math.PI / this.period);
7+
},
8+
/// Get time of next high/low/ tide after given time. Returns {v:value,t:time}
9+
getNext : function(time,isHigh/*bool/undefined*/) {
10+
var v = Math.ceil(2*(time-this.offset) / this.period)/2;
11+
if (isHigh===undefined) {
12+
isHigh = (v-Math.floor(v))==0;
13+
} else {
14+
if (isHigh) v = Math.ceil(v);
15+
else v = Math.floor(v)+0.5;
16+
}
17+
return {v:isHigh?1:0, t:this.offset+v*this.period};
18+
},
19+
save : function() {
20+
let s = require("Storage").readJSON("tidetimes.json",1)||{};
21+
s.offset = this.offset;
22+
s.period = this.period;
23+
require("Storage").writeJSON("tidetimes.json",s);
24+
},
25+
load : function() {
26+
let s = require("Storage").readJSON("tidetimes.json",1)||{};
27+
this.offset = 0|s.offset;
28+
if (s.period) this.period = 0|s.period;
29+
}
30+
};
31+
exports.load();

apps/tidetimes/metadata.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{ "id": "tidetimes",
2+
"name": "Tide Times",
3+
"shortName":"Tides",
4+
"version":"0.01",
5+
"author": "gfwilliams",
6+
"description": "A simple app to keep track of tide times. Set up the time of the next high/low tide, and this will show you the next high/low tides based on a 6hr 12min interval",
7+
"icon": "app.png",
8+
"tags": "outdoor,sea,tide,clkinfo",
9+
"supports" : ["BANGLEJS2"],
10+
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
11+
"provides_modules": ["tidetimes"],
12+
"readme": "README.md",
13+
"storage": [
14+
{"name":"tidetimes","url":"lib.js"},
15+
{"name":"tidetimes.app.js","url":"app.js"},
16+
{"name":"tidetimes.clkinfo.js","url":"clkinfo.js"},
17+
{"name":"tidetimes.img","url":"app-icon.js","evaluate":true}
18+
], "data": [
19+
{"name":"tidetimes.json" }
20+
]
21+
}

0 commit comments

Comments
 (0)