Merge remote-tracking branch 'upstream/master'

pull/1241/head
David Peer 2022-01-11 17:27:45 +01:00
commit d260e7c507
30 changed files with 882 additions and 31 deletions

View File

@ -1351,6 +1351,22 @@
{"name":"pparrot.img","url":"party-parrot-icon.js","evaluate":true} {"name":"pparrot.img","url":"party-parrot-icon.js","evaluate":true}
] ]
}, },
{
"id": "hralarm",
"name": "Heart rate alarm",
"shortName":"HR Alarm",
"version":"0.01",
"description": "This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits",
"icon": "widget.png",
"type": "widget",
"tags": "widget",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"hralarm.wid.js","url":"widget.js"},
{"name":"hralarm.settings.js","url":"settings.js"}
]
},
{ {
"id": "hrings", "id": "hrings",
"name": "Hypno Rings", "name": "Hypno Rings",
@ -1504,7 +1520,7 @@
{ {
"id": "gpsinfo", "id": "gpsinfo",
"name": "GPS Info", "name": "GPS Info",
"version": "0.08", "version": "0.09",
"description": "An application that displays information about altitude, lat/lon, satellites and time", "description": "An application that displays information about altitude, lat/lon, satellites and time",
"icon": "gps-info.png", "icon": "gps-info.png",
"type": "app", "type": "app",
@ -4498,7 +4514,7 @@
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",
"version":"0.10", "version":"0.11",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.", "description": "Library Computer Access Retrieval System (LCARS) clock.",
@ -5046,7 +5062,7 @@
{ {
"id": "lapcounter", "id": "lapcounter",
"name": "Lap Counter", "name": "Lap Counter",
"version": "0.01", "version": "0.02",
"description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).", "description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],
@ -5081,7 +5097,7 @@
{ "id": "circlesclock", { "id": "circlesclock",
"name": "Circles clock", "name": "Circles clock",
"shortName":"Circles clock", "shortName":"Circles clock",
"version":"0.04", "version":"0.05",
"description": "A clock with circles for different data at the bottom in a probably familiar style", "description": "A clock with circles for different data at the bottom in a probably familiar style",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}], "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}],
@ -5132,6 +5148,25 @@
{"name":"ltherm.img","url":"icon.js","evaluate":true} {"name":"ltherm.img","url":"icon.js","evaluate":true}
] ]
}, },
{
"id": "ftclock",
"name": "Four Twenty Clock",
"version": "0.01",
"description": "A clock that tells when and where it's going to be 4:20 next",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{"name":"ftclock.app.js","url":"app.js"},
{"name":"fourTwenty","url":"fourTwenty.js"},
{"name":"fourTwentyTz","url":"fourTwentyTz.js"},
{"name":"ftclock.img","url":"app-icon.js","evaluate":true}
]
},
{ {
"id": "mmind", "id": "mmind",
"name": "Classic Mind Game", "name": "Classic Mind Game",

View File

@ -40,7 +40,10 @@ The main menu contains several settings covering Anton clock in general.
* **Show Weekday** - Weekday is shown in the time presentation without seconds. * **Show Weekday** - Weekday is shown in the time presentation without seconds.
Weekday name depends on the current locale. Weekday name depends on the current locale.
If seconds are shown, the weekday is never shown as there is not enough space on the watch face. If seconds are shown, the weekday is never shown as there is not enough space on the watch face.
**Show Weeknumber** - Weeknumber (ISO-8601) is shown. * **Show Weeknumber** - Week-number (ISO-8601) is shown. (default: Off)
If "Show Weekday" is "Off" the week-number is displayed as "week #:<num>".
If "Show Weekday" is "On" the weekday name is cut at 6th position and suffixed with ".#<week num>".
If seconds are shown, the week number is never shown as there is not enough space on the watch face.
* **Vector font** - Use the built-in vector font for dates and weekday. * **Vector font** - Use the built-in vector font for dates and weekday.
This can improve readability. This can improve readability.
Otherwise, a scaled version of the built-in 6x8 pixels font is used. Otherwise, a scaled version of the built-in 6x8 pixels font is used.

View File

@ -188,9 +188,8 @@ function draw() {
g.drawString(dateStr, x, y); g.drawString(dateStr, x, y);
if (weekDay || calWeek) { if (weekDay || calWeek) {
var dowwumStr = require("locale").dow(date); var dowwumStr = require("locale").dow(date);
dowwumStr = "thursday";
if (calWeek) if (calWeek)
dowwumStr = (weekDay ? dowwumStr.substr(0,Math.min(dowwumStr.length,6)) + (dowwumStr.length>=6 ? "." : "") : "week ") + "#" + ISO8601calWeek(date); dowwumStr = (weekDay ? dowwumStr.substr(0,Math.min(dowwumStr.length,6)) + (dowwumStr.length>=6 ? "." : "") : "week ") + "#" + ISO8601calWeek(date); //TODO: locale for "week"
if (upperCase) if (upperCase)
dowwumStr = dowwumStr.toUpperCase(); dowwumStr = dowwumStr.toUpperCase();
g.drawString(dowwumStr, x, y + (vectorFont ? 26 : 16)); g.drawString(dowwumStr, x, y + (vectorFont ? 26 : 16));

View File

@ -5,3 +5,5 @@
Add step distance and weather Add step distance and weather
Allow switching visibility of widgets Allow switching visibility of widgets
Make circles and text slightly bigger Make circles and text slightly bigger
0.05: Show correct percentage values in circles
Show humidity as weather circle data

View File

@ -10,6 +10,9 @@ It can show the following information (this can be configured):
* Heart rate (automatically updates when screen is on and unlocked) * Heart rate (automatically updates when screen is on and unlocked)
* Battery (including charging status and battery low warning) * Battery (including charging status and battery low warning)
* Weather (requires [weather app](https://banglejs.com/apps/#weather)) * Weather (requires [weather app](https://banglejs.com/apps/#weather))
* Humidity as circle progress
* Temperature inside circle
* Condition as icon below circle
## Screenshots ## Screenshots
![Screenshot dark theme](screenshot-dark.png) ![Screenshot dark theme](screenshot-dark.png)

View File

@ -283,6 +283,7 @@ function drawWeather(w) {
if (!w) w = getCirclePosition("weather"); if (!w) w = getCirclePosition("weather");
const weather = getWeather(); const weather = getWeather();
const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined; const tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
const humidity = weather ? weather.hum : undefined;
const code = weather ? weather.code : -1; const code = weather ? weather.code : -1;
// Draw rectangle background: // Draw rectangle background:
@ -292,6 +293,10 @@ function drawWeather(w) {
g.setColor(colorGrey); g.setColor(colorGrey);
g.fillCircle(w, h3, radiusOuter); g.fillCircle(w, h3, radiusOuter);
if (humidity >= 0) {
drawGauge(w, h3, humidity / 100, colorYellow);
}
g.setColor(colorBg); g.setColor(colorBg);
g.fillCircle(w, h3, radiusInner); g.fillCircle(w, h3, radiusInner);
@ -363,22 +368,21 @@ function radians(a) {
} }
function drawGauge(cx, cy, percent, color) { function drawGauge(cx, cy, percent, color) {
let offset = 30; const offset = 15;
let end = 300; const end = 345;
var i = 0; const r = radiusInner + 3;
var r = radiusInner + 3;
if (percent <= 0) return; if (percent <= 0) return;
if (percent > 1) percent = 1; if (percent > 1) percent = 1;
var startrot = -offset; const startrot = -offset;
var endrot = startrot - ((end - offset) * percent) - 35; const endrot = startrot - ((end - offset) * percent);
g.setColor(color); g.setColor(color);
const size = radiusOuter - radiusInner - 2; const size = radiusOuter - radiusInner - 2;
// draw gauge // draw gauge
for (i = startrot; i > endrot - size; i -= size) { for (let i = startrot; i > endrot - size; i -= size) {
x = cx + r * Math.sin(radians(i)); x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i)); y = cy + r * Math.cos(radians(i));
g.fillCircle(x, y, size); g.fillCircle(x, y, size);

4
apps/ftclock/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
timezonedb.csv.zip
country.csv
zone.csv
timezone.csv

1
apps/ftclock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: first release

24
apps/ftclock/README.md Normal file
View File

@ -0,0 +1,24 @@
# Four Twenty Clock
A clock that tells when and where it's going to be [4:20](https://en.wikipedia.org/wiki/420_%28cannabis_culture%29) next
![screensot](screenshot.png) ![screenshot at 4:20](screenshot1.png)
## Generating `fourTwentyTz.js`
Once in a while we need to regenerate it for 2 reasons:
* One or more places got in or out of daylight saving time (DST) mode.
* The database saying _when_ places enter/exit DST mode got updated.
I'll do my best to release a new version every time this happens,
but if you ever need to do this yourself, here's how:
* `cd` to the `ftclock` folder
* If you haven't done so yet, run `npm install` there (this would create the `node_modules` folder).
* Get and unzip the latest `timezone.csv.zip` from https://timezonedb.com/download
* Run `npm run make`
## Creator
[Nimrod Kerrett](zzzen.com)

1
apps/ftclock/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwghC/AH4A/AH4A/AAMHu4ACuwHBs4HDsEGBIQLCsADBgwPDCAQGEuwXFBwI0GEAMHuAGCCoMHC4pMHEAIXEAgIGEBwI9BC4wSCC8IVCMAwIBs4XKUQJfITQgXCDwp8EHAqaECoLFEu4cDBIggBs6uFZozuGBAVmC4g+FMgZQEZQ5vGC4iRIC5IrDN4h5EC5J3BCoIKGgyaEC44VBC46yEDgoeDgxqLC5SCMAgoTFY47GFC4xFBdwwPBD4oWFAH4A/AH4A/AH4AjA=="))

52
apps/ftclock/app.js Normal file
View File

@ -0,0 +1,52 @@
let getNextFourTwenty = require("fourTwenty").getNextFourTwenty;
require("FontTeletext10x18Ascii").add(Graphics);
let leaf_img = "\x17\x18\x81\x00\x00\x10\x00\x00 \x00\x00@\x00\x01\xc0\x00\x03\x80\x00\x0f\x80\x00\x1f\x00\x00>\x00\x00|\x00\xc0\xf8\x19\xe1\xf0\xf1\xe3\xe3\xc3\xf7\xdf\x83\xff\xfe\x03\xff\xf8\x03\xff\xe0\x03\xff\x80\x03\xfe\x00\x7f\xff\xc0\xff\xff\xc0\x06\xe0\x00\x18\xc0\x00 \x80\x00\x00\x00";
// timeout used to update every minute
let drawTimeout;
// schedule a draw for the next minute
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
}
function draw() {
g.reset();
g.setBgColor("#ffffff");
let date = new Date();
let timeStr = require("locale").time(date,1);
let next420 = getNextFourTwenty();
g.clearRect(0,26,g.getWidth(),g.getHeight());
g.setColor("#00ff00").setFontAlign(0,-1).setFont("Teletext10x18Ascii",2);
g.drawString(next420.minutes? timeStr: `\0${leaf_img}${timeStr}\0${leaf_img}`, g.getWidth()/2, 28);
g.setColor("#000000");
g.setFontAlign(-1,-1).setFont("Teletext10x18Ascii");
g.drawString(g.wrapString(next420.text, g.getWidth()-8).join("\n"),4,60);
// queue draw in one minute
queueDraw();
}
// Clear the screen once, at startup
g.clear();
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
// draw immediately at first, queue update
draw();
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
// Show launcher when middle button pressed
Bangle.setUI("clock");

BIN
apps/ftclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,45 @@
let timezones = require("fourTwentyTz").timezones;
function get420offset() {
let current_time = Math.floor((Date.now()%(24*3600*1000))/60000);
let current_min = current_time%60;
if (current_min>20 && current_min<25) {
current_time -= current_min-20; // 5 minutes grace period
}
let offset = 16*60+20-current_time;
if (offset<0) {
offset += 24*60;
}
return offset;
}
function makeFourTwentyText(minutes, places) {
//let plural = minutes==1? "": "s";
//let msgprefix = minutes? `${minutes} minute${plural} to`: "It is now";
let msgprefix = minutes? `${minutes}m to`: "It is now";
let msgsuffix = places.length>1? ", and other fine places": "";
let msgplace = places[Math.floor(Math.random()*places.length)];
return `${msgprefix} 4:20 at ${msgplace}${msgsuffix}.`;
}
function getNextFourTwenty() {
let offs = get420offset();
for (let i=0; i<timezones.length; i++) {
if (timezones[i][0]<=offs) {
let minutes = offs-timezones[i][0];
let places = timezones[i][1];
return {
minutes: minutes,
places: places,
text: makeFourTwentyText(minutes, places)
};
}
}
return {
minutes: 666,
places: ["Snafu (Yes. It's a bug)"],
text: "Snafu (Yes. It's a bug)"
};
}
exports.getNextFourTwenty = getNextFourTwenty;

View File

@ -0,0 +1,463 @@
// Generated by mkFourTwentyTz.js
// Data source: https://timezonedb.com/files/timezonedb.csv.zip
// Sun Jan 09 2022 13:21:47 GMT+0200 (Israel Standard Time)
exports.timezones = {
"0": [
"Troll, Antarctica",
"Ouagadougou, Burkina Faso",
"Abidjan, Côte d'Ivoire",
"Canary, Spain",
"Faroe, Faroe Islands",
"London, United Kingdom of Great Britain and Northern Ireland",
"Guernsey, Guernsey",
"Accra, Ghana",
"Danmarkshavn, Greenland",
"Banjul, Gambia",
"Conakry, Guinea",
"Bissau, Guinea-Bissau",
"Dublin, Ireland",
"Isle of_Man, Isle of Man",
"Reykjavik, Iceland",
"Jersey, Jersey",
"Monrovia, Liberia",
"Bamako, Mali",
"Nouakchott, Mauritania",
"Lisbon, Portugal",
"Madeira, Portugal",
"St Helena, Saint Helena, Ascension and Tristan da Cunha",
"Freetown, Sierra Leone",
"Dakar, Senegal",
"Sao Tome, Sao Tome and Principe",
"Lome, Togo"
],
"60": [
"Andorra, Andorra",
"Tirane, Albania",
"Luanda, Angola",
"Vienna, Austria",
"Sarajevo, Bosnia and Herzegovina",
"Brussels, Belgium",
"Porto-Novo, Benin",
"Kinshasa, Congo, Democratic Republic of the",
"Bangui, Central African Republic",
"Brazzaville, Congo",
"Zurich, Switzerland",
"Douala, Cameroon",
"Prague, Czechia",
"Berlin, Germany",
"Busingen, Germany",
"Copenhagen, Denmark",
"Algiers, Algeria",
"El Aaiun, Western Sahara",
"Madrid, Spain",
"Ceuta, Spain",
"Paris, France",
"Libreville, Gabon",
"Gibraltar, Gibraltar",
"Malabo, Equatorial Guinea",
"Zagreb, Croatia",
"Budapest, Hungary",
"Rome, Italy",
"Vaduz, Liechtenstein",
"Luxembourg, Luxembourg",
"Casablanca, Morocco",
"Monaco, Monaco",
"Podgorica, Montenegro",
"Skopje, North Macedonia",
"Malta, Malta",
"Niamey, Niger",
"Lagos, Nigeria",
"Amsterdam, Netherlands",
"Oslo, Norway",
"Warsaw, Poland",
"Belgrade, Serbia",
"Stockholm, Sweden",
"Ljubljana, Slovenia",
"Longyearbyen, Svalbard and Jan Mayen",
"Bratislava, Slovakia",
"San Marino, San Marino",
"Ndjamena, Chad",
"Tunis, Tunisia",
"Vatican, Holy See"
],
"120": [
"Mariehamn, Åland Islands",
"Sofia, Bulgaria",
"Bujumbura, Burundi",
"Gaborone, Botswana",
"Lubumbashi, Congo, Democratic Republic of the",
"Nicosia, Cyprus",
"Famagusta, Cyprus",
"Tallinn, Estonia",
"Cairo, Egypt",
"Helsinki, Finland",
"Athens, Greece",
"Jerusalem, Israel",
"Amman, Jordan",
"Beirut, Lebanon",
"Maseru, Lesotho",
"Vilnius, Lithuania",
"Riga, Latvia",
"Tripoli, Libya",
"Chisinau, Moldova, Republic of",
"Blantyre, Malawi",
"Maputo, Mozambique",
"Windhoek, Namibia",
"Gaza, Palestine, State of",
"Hebron, Palestine, State of",
"Bucharest, Romania",
"Kaliningrad, Russian Federation",
"Kigali, Rwanda",
"Khartoum, Sudan",
"Juba, South Sudan",
"Damascus, Syrian Arab Republic",
"Mbabane, Eswatini",
"Kiev, Ukraine",
"Uzhgorod, Ukraine",
"Zaporozhye, Ukraine",
"Johannesburg, South Africa",
"Lusaka, Zambia",
"Harare, Zimbabwe"
],
"180": [
"Syowa, Antarctica",
"Bahrain, Bahrain",
"Minsk, Belarus",
"Djibouti, Djibouti",
"Asmara, Eritrea",
"Addis Ababa, Ethiopia",
"Baghdad, Iraq",
"Nairobi, Kenya",
"Comoro, Comoros",
"Kuwait, Kuwait",
"Antananarivo, Madagascar",
"Qatar, Qatar",
"Moscow, Russian Federation",
"Simferopol, Ukraine",
"Kirov, Russian Federation",
"Volgograd, Russian Federation",
"Riyadh, Saudi Arabia",
"Mogadishu, Somalia",
"Istanbul, Turkey",
"Dar es_Salaam, Tanzania, United Republic of",
"Kampala, Uganda",
"Aden, Yemen",
"Mayotte, Mayotte"
],
"240": [
"Dubai, United Arab Emirates",
"Yerevan, Armenia",
"Baku, Azerbaijan",
"Tbilisi, Georgia",
"Mauritius, Mauritius",
"Muscat, Oman",
"Reunion, Réunion",
"Astrakhan, Russian Federation",
"Saratov, Russian Federation",
"Ulyanovsk, Russian Federation",
"Samara, Russian Federation",
"Mahe, Seychelles"
],
"300": [
"Mawson, Antarctica",
"Qyzylorda, Kazakhstan",
"Aqtobe, Kazakhstan",
"Aqtau, Kazakhstan",
"Atyrau, Kazakhstan",
"Oral, Kazakhstan",
"Maldives, Maldives",
"Karachi, Pakistan",
"Yekaterinburg, Russian Federation",
"Kerguelen, French Southern Territories",
"Dushanbe, Tajikistan",
"Ashgabat, Turkmenistan",
"Samarkand, Uzbekistan",
"Tashkent, Uzbekistan"
],
"360": [
"Vostok, Antarctica",
"Dhaka, Bangladesh",
"Thimphu, Bhutan",
"Urumqi, China",
"Chagos, British Indian Ocean Territory",
"Bishkek, Kyrgyzstan",
"Almaty, Kazakhstan",
"Qostanay, Kazakhstan",
"Omsk, Russian Federation"
],
"420": [
"Davis, Antarctica",
"Christmas, Christmas Island",
"Jakarta, Indonesia",
"Pontianak, Indonesia",
"Phnom Penh, Cambodia",
"Vientiane, Lao People's Democratic Republic",
"Hovd, Mongolia",
"Novosibirsk, Russian Federation",
"Barnaul, Russian Federation",
"Tomsk, Russian Federation",
"Novokuznetsk, Russian Federation",
"Krasnoyarsk, Russian Federation",
"Bangkok, Thailand",
"Ho Chi_Minh, Viet Nam"
],
"480": [
"Perth, Australia",
"Brunei, Brunei Darussalam",
"Shanghai, China",
"Hong Kong, Hong Kong",
"Makassar, Indonesia",
"Ulaanbaatar, Mongolia",
"Choibalsan, Mongolia",
"Macau, Macao",
"Kuala Lumpur, Malaysia",
"Kuching, Malaysia",
"Manila, Philippines",
"Irkutsk, Russian Federation",
"Singapore, Singapore",
"Taipei, Taiwan, Province of China"
],
"540": [
"Jayapura, Indonesia",
"Tokyo, Japan",
"Pyongyang, Korea (Democratic People's Republic of)",
"Seoul, Korea, Republic of",
"Palau, Palau",
"Chita, Russian Federation",
"Yakutsk, Russian Federation",
"Khandyga, Russian Federation",
"Dili, Timor-Leste"
],
"600": [
"DumontDUrville, Antarctica",
"Brisbane, Australia",
"Lindeman, Australia",
"Chuuk, Micronesia (Federated States of)",
"Guam, Guam",
"Saipan, Northern Mariana Islands",
"Port Moresby, Papua New Guinea",
"Vladivostok, Russian Federation",
"Ust-Nera, Russian Federation"
],
"660": [
"Casey, Antarctica",
"Lord Howe, Australia",
"Macquarie, Australia",
"Hobart, Australia",
"Melbourne, Australia",
"Sydney, Australia",
"Pohnpei, Micronesia (Federated States of)",
"Kosrae, Micronesia (Federated States of)",
"Noumea, New Caledonia",
"Bougainville, Papua New Guinea",
"Magadan, Russian Federation",
"Sakhalin, Russian Federation",
"Srednekolymsk, Russian Federation",
"Guadalcanal, Solomon Islands",
"Efate, Vanuatu"
],
"720": [
"Tarawa, Kiribati",
"Majuro, Marshall Islands",
"Kwajalein, Marshall Islands",
"Norfolk, Norfolk Island",
"Nauru, Nauru",
"Kamchatka, Russian Federation",
"Anadyr, Russian Federation",
"Funafuti, Tuvalu",
"Wake, United States Minor Outlying Islands",
"Wallis, Wallis and Futuna"
],
"780": [
"McMurdo, Antarctica",
"Pago Pago, American Samoa",
"Fiji, Fiji",
"Kanton, Kiribati",
"Niue, Niue",
"Auckland, New Zealand",
"Fakaofo, Tokelau",
"Tongatapu, Tonga",
"Midway, United States Minor Outlying Islands",
"Apia, Samoa"
],
"840": [
"Rarotonga, Cook Islands",
"Kiritimati, Kiribati",
"Tahiti, French Polynesia",
"Adak, United States of America",
"Honolulu, United States of America"
],
"900": [
"Gambier, French Polynesia",
"Anchorage, United States of America",
"Juneau, United States of America",
"Sitka, United States of America",
"Metlakatla, United States of America",
"Yakutat, United States of America",
"Nome, United States of America"
],
"960": [
"Vancouver, Canada",
"Tijuana, Mexico",
"Pitcairn, Pitcairn",
"Los Angeles, United States of America"
],
"1020": [
"Edmonton, Canada",
"Cambridge Bay, Canada",
"Yellowknife, Canada",
"Inuvik, Canada",
"Creston, Canada",
"Dawson Creek, Canada",
"Fort Nelson, Canada",
"Whitehorse, Canada",
"Dawson, Canada",
"Mazatlan, Mexico",
"Chihuahua, Mexico",
"Ojinaga, Mexico",
"Hermosillo, Mexico",
"Denver, United States of America",
"Boise, United States of America",
"Phoenix, United States of America"
],
"1080": [
"Belize, Belize",
"Winnipeg, Canada",
"Rainy River, Canada",
"Resolute, Canada",
"Rankin Inlet, Canada",
"Regina, Canada",
"Swift Current, Canada",
"Costa Rica, Costa Rica",
"Galapagos, Ecuador",
"Guatemala, Guatemala",
"Tegucigalpa, Honduras",
"Mexico City, Mexico",
"Merida, Mexico",
"Monterrey, Mexico",
"Matamoros, Mexico",
"Bahia Banderas, Mexico",
"Managua, Nicaragua",
"El Salvador, El Salvador",
"Chicago, United States of America",
"Tell City, Indiana",
"Knox, Indiana",
"Menominee, United States of America",
"Center, North Dakota",
"New_Salem, North Dakota",
"Beulah, North Dakota"
],
"1140": [
"Eirunepe, Brazil",
"Rio Branco, Brazil",
"Nassau, Bahamas",
"Toronto, Canada",
"Nipigon, Canada",
"Thunder Bay, Canada",
"Iqaluit, Canada",
"Pangnirtung, Canada",
"Atikokan, Canada",
"Easter, Chile",
"Bogota, Colombia",
"Havana, Cuba",
"Guayaquil, Ecuador",
"Port-au-Prince, Haiti",
"Jamaica, Jamaica",
"Cayman, Cayman Islands",
"Cancun, Mexico",
"Panama, Panama",
"Lima, Peru",
"Grand Turk, Turks and Caicos Islands",
"New York, United States of America",
"Detroit, United States of America",
"Louisville, Kentucky",
"Monticello, Kentucky",
"Indianapolis, Indiana",
"Vincennes, Indiana",
"Winamac, Indiana",
"Marengo, Indiana",
"Petersburg, Indiana",
"Vevay, Indiana"
],
"1200": [
"Antigua, Antigua and Barbuda",
"Anguilla, Anguilla",
"Aruba, Aruba",
"Barbados, Barbados",
"St Barthelemy, Saint Barthélemy",
"Bermuda, Bermuda",
"La Paz, Bolivia (Plurinational State of)",
"Kralendijk, Bonaire, Sint Eustatius and Saba",
"Campo Grande, Brazil",
"Cuiaba, Brazil",
"Porto Velho, Brazil",
"Boa Vista, Brazil",
"Manaus, Brazil",
"Halifax, Canada",
"Glace Bay, Canada",
"Moncton, Canada",
"Goose Bay, Canada",
"Blanc-Sablon, Canada",
"Curacao, Curaçao",
"Dominica, Dominica",
"Santo Domingo, Dominican Republic",
"Grenada, Grenada",
"Thule, Greenland",
"Guadeloupe, Guadeloupe",
"Guyana, Guyana",
"St Kitts, Saint Kitts and Nevis",
"St Lucia, Saint Lucia",
"Marigot, Saint Martin (French part)",
"Martinique, Martinique",
"Montserrat, Montserrat",
"Puerto Rico, Puerto Rico",
"Lower Princes, Sint Maarten (Dutch part)",
"Port of_Spain, Trinidad and Tobago",
"St Vincent, Saint Vincent and the Grenadines",
"Caracas, Venezuela (Bolivarian Republic of)",
"Tortola, Virgin Islands (British)",
"St Thomas, Virgin Islands (U.S.)"
],
"1260": [
"Palmer, Antarctica",
"Rothera, Antarctica",
"Buenos Aires, Argentina",
"Cordoba, Argentina",
"Salta, Argentina",
"Jujuy, Argentina",
"Tucuman, Argentina",
"Catamarca, Argentina",
"La Rioja, Argentina",
"San Juan, Argentina",
"Mendoza, Argentina",
"San Luis, Argentina",
"Rio Gallegos, Argentina",
"Ushuaia, Argentina",
"Belem, Brazil",
"Fortaleza, Brazil",
"Recife, Brazil",
"Araguaina, Brazil",
"Maceio, Brazil",
"Bahia, Brazil",
"Sao Paulo, Brazil",
"Santarem, Brazil",
"Santiago, Chile",
"Punta Arenas, Chile",
"Stanley, Falkland Islands (Malvinas)",
"Cayenne, French Guiana",
"Nuuk, Greenland",
"Miquelon, Saint Pierre and Miquelon",
"Asuncion, Paraguay",
"Paramaribo, Suriname",
"Montevideo, Uruguay"
],
"1320": [
"Noronha, Brazil",
"South Georgia, South Georgia and the South Sandwich Islands"
],
"1380": [
"Cape Verde, Cabo Verde",
"Scoresbysund, Greenland",
"Azores, Portugal"
]
}

View File

@ -0,0 +1,74 @@
let fs = require('fs');
let csv = require('csv');
let countries = {},
zones = {},
offsdict = {},
now = Date.now(); // we need this to find zone's current DST state
function handleWrite(err,bytes) {
if (err) {
console.log(`Error writing to file ${err}`);
}
}
console.log("Generating fourTwentyTz.js...");
fs.createReadStream(__dirname+'/country.csv')
.pipe(csv.parse())
.on('data', (r) => {
countries[r[0]] = r[1];
})
.on('end', () => {
fs.createReadStream(__dirname+'/zone.csv')
.pipe(csv.parse())
.on('data', (r) => {
let parts = r[2].replace('_',' ').split('/');
let city = parts[parts.length-1];
let country ='';
if (parts.length>2) { // e.g. America/North_Dakota/New_Salem
country = parts[1]; // e.g. North Dakota
} else {
country = countries[r[1]]; // e.g. United States
}
zones[parseInt(r[0])] = {"name": `${city}, ${country}`};
})
.on('end', () => {
fs.createReadStream(__dirname+'/timezone.csv')
.pipe(csv.parse())
.on('data', (r) => {
code = parseInt(r[0]);
if (!(code in zones)) return;
starttime = parseInt(r[2] || "0"); // Bugger. They're feeding us blanks for UTC now
offs = parseInt(r[3]);
if (offs<0) {
offs += 60*60*24;
}
zone = zones[code];
if (starttime<now && (!("starttime" in zone) || zone.starttime<starttime)) {
zone.starttime = starttime;
zone.offs = Math.floor(offs/60);
}
})
.on('end', () => {
for (z in zones) {
zone = zones[z];
if (zone.offs%60) continue; // One a dem funky timezones. Ignore.
zonelist = offsdict[zone.offs] || [];
zonelist.push(zone.name);
offsdict[zone.offs] = zonelist;
}
fs.open("fourTwentyTz.js","w", (err, fd) => {
if (err) {
console.log("Can't open output file");
return;
}
fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite);
fs.write(fd, `// ${Date()}\n`, handleWrite);
fs.write(fd, "// Data source: https://timezonedb.com/files/timezonedb.csv.zip\n", handleWrite);
fs.write(fd, "exports.timezones = ", handleWrite);
fs.write(fd, JSON.stringify(offsdict, null, 4), handleWrite);
console.log('Done.');
});
})
})
});

15
apps/ftclock/package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "mkfourtwentytz",
"version": "1.0.0",
"description": "Convert timezonedb.com CSV to fourTwentyTz.js for BangleJS ftclock app",
"main": "mkFourTwentyTz.js",
"scripts": {
"make": "node mkFourTwentyTz.js"
},
"keywords": [],
"author": "",
"license": "GPL-3.0",
"dependencies": {
"csv": "^6.0.5"
}
}

BIN
apps/ftclock/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -5,3 +5,4 @@
0.06: Add number of satellites in view and fix crash with GPS time 0.06: Add number of satellites in view and fix crash with GPS time
0.07: Resolve one FIFO_FULL case and exit App with button press 0.07: Resolve one FIFO_FULL case and exit App with button press
0.08: Leave GPS power switched on on exit (will switch off after 0.5 seconds anyway) 0.08: Leave GPS power switched on on exit (will switch off after 0.5 seconds anyway)
0.09: Fix FIFO_FULL error

View File

@ -4,7 +4,7 @@ function satelliteImage() {
var Layout = require("Layout"); var Layout = require("Layout");
var layout; var layout;
Bangle.setGPSPower(1, "app"); //Bangle.setGPSPower(1, "app");
E.showMessage("Loading..."); // avoid showing rubbish on screen E.showMessage("Loading..."); // avoid showing rubbish on screen
var lastFix = { var lastFix = {
@ -19,7 +19,7 @@ var lastFix = {
var SATinView = 0; var SATinView = 0;
var nofBD = 0; var nofBD = 0;
var nofGP = 0; var nofGP = 0;
var listenerGPSraw = 1; var listenerGPSraw = 0;
function formatTime(now) { function formatTime(now) {
if (now == undefined) { if (now == undefined) {
@ -87,12 +87,12 @@ function onGPS(fix) {
{type:"txt", font:"6x8", pad:3, label:"Satellites used" } {type:"txt", font:"6x8", pad:3, label:"Satellites used" }
]}, ]},
{type:"txt", font:"6x8", label:"", fillx:true, id:"progress" } {type:"txt", font:"6x8", label:"", fillx:true, id:"progress" }
]},{lazy:true}); ]},{lazy:false});
} }
g.clearRect(0,24,g.getWidth(),g.getHeight()); g.clearRect(0,24,g.getWidth(),g.getHeight());
layout.render(); layout.render();
} }
lastFix = fix; //lastFix = fix;
if (fix.fix) { if (fix.fix) {
if (listenerGPSraw == 1) { if (listenerGPSraw == 1) {
Bangle.removeListener('GPS-raw', onGPSraw); Bangle.removeListener('GPS-raw', onGPSraw);
@ -108,15 +108,28 @@ function onGPS(fix) {
layout.time.label = "Time: "+formatTime(fix.time); layout.time.label = "Time: "+formatTime(fix.time);
layout.sat.label = "Satellites: "+satellites; layout.sat.label = "Satellites: "+satellites;
layout.maidenhead.label = "Maidenhead: "+maidenhead; layout.maidenhead.label = "Maidenhead: "+maidenhead;
layout.render();
} else { } else {
if (listenerGPSraw == 0) { if (fix.satelites != lastFix.satelites) {
Bangle.on('GPS-raw', onGPSraw); layout.clear(layout.sat);
listenerGPSraw = 1; layout.sat.label = fix.satellites;
layout.render(layout.sat);
}
if (SATinView != lastFix.SATinView) {
layout.clear(layout.progress);
layout.progress.label = "in view: " + SATinView;
layout.render(layout.progress);
} }
layout.sat.label = fix.satellites;
layout.progress.label = "in view: " + SATinView;
} }
layout.render(); //layout.render();
if (listenerGPSraw == 0 && !fix.fix) {
setTimeout(() => Bangle.on('GPS-raw', onGPSraw), 10);
listenerGPSraw = 1;
}
lastFix = fix;
lastFix.SATinView = SATinView;
} }
function onGPSraw(nmea) { function onGPSraw(nmea) {
@ -129,7 +142,8 @@ function onGPSraw(nmea) {
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
Bangle.on('GPS', onGPS); Bangle.on('GPS', onGPS);
Bangle.on('GPS-raw', onGPSraw); //Bangle.on('GPS-raw', onGPSraw);
Bangle.setGPSPower(1, "app");
function exitApp() { function exitApp() {
load(); load();

1
apps/hralarm/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New Widget!

15
apps/hralarm/README.md Normal file
View File

@ -0,0 +1,15 @@
# Heart rate alarm
This invisible widget vibrates whenever the heart rate gets close to the upper limit or goes over or under the configured limits.
## Usage
Configure the heart rate limits in the apps settings. This widget uses both 'HRM' and 'BTHRM' events.
## Features
Long vibration every 10 seconds on reaching upper limit, short vibrations between upper limit and warning threshold and an single vibration when reaching the lower limit again.
## Requests/Creator
https://github.com/halemmerich

57
apps/hralarm/settings.js Normal file
View File

@ -0,0 +1,57 @@
(function(back) {
var FILE = "hralarm.json";
var settings = Object.assign({
enabled: false,
upper: 180,
warning: 170,
lower: 150,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
'': { 'title': 'HR Alarm' },
'< Back': back,
'Enabled': {
value: !!settings.enabled,
format: v => settings.enabled ? "On" : "Off",
onchange: v => {
settings.enabled = v;
writeSettings();
}
},
'Upper limit': {
value: settings.upper,
min: 0,
step:5,
max: 300,
onchange: v => {
settings.upper = v;
writeSettings();
}
},
'Lower limit': {
value: settings.lower,
min: 0,
step:5,
max: 300,
onchange: v => {
settings.lower = v;
writeSettings();
}
},
'Warning at': {
value: settings.warning,
min: 0,
step:5,
max: 300,
onchange: v => {
settings.warning = v;
writeSettings();
}
}
});
})

27
apps/hralarm/widget.js Normal file
View File

@ -0,0 +1,27 @@
(() => {
var settings = require('Storage').readJSON("hralarm.json", true) || {};
if (!settings.enabled){ Bangle.setHRMPower(0, 'hralarm'); return; }
Bangle.setHRMPower(1, 'hralarm');
var hitLimit = 0;
var checkHr = function(hr){
if (hr.bpm > settings.warning && hr.bpm <= settings.upper){
Bangle.buzz(100, 1);
}
if (hitLimit < getTime() && hr.bpm > settings.upper){
hitLimit = getTime() + 10;
Bangle.buzz(2000, 1);
}
if (hitLimit > 0 && hr.bpm < settings.lower){
hitLimit = 0;
Bangle.buzz(500, 1);
}
};
Bangle.on("HRM", checkHr);
Bangle.on("BTHRM", checkHr);
WIDGETS["hralarm"]={
area:"tl",
width: 0,
draw: function(){}
};
})()

BIN
apps/hralarm/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +1,2 @@
0.01: first release 0.01: first release
0.02: Themeable app icon

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap")) require("heatshrink").decompress(atob("mEwwI0xg+evPsAon+ApX8Aon4AonwAod78AFDv4FWvoFE/IFDz4FXvIFD3wFE/wFW7wFDh5xBAoUfAok/Aol/BZUXAogA6A="))

View File

@ -7,4 +7,5 @@
0.07: Added settings to adjust data that is shown for each row. 0.07: Added settings to adjust data that is shown for each row.
0.08: Support for multiple screens. 24h graph for steps + HRM. Fullscreen Mode. 0.08: Support for multiple screens. 24h graph for steps + HRM. Fullscreen Mode.
0.09: Tab anywhere to open the launcher. 0.09: Tab anywhere to open the launcher.
0.10: Fix - Clock is unresponsive, if gadgetbridge connects. 0.10: Fix - Clock is unresponsive, if gadgetbridge connects.
0.11: Added getting the gadgetbridge weather

View File

@ -1,5 +1,6 @@
const SETTINGS_FILE = "lcars.setting.json"; const SETTINGS_FILE = "lcars.setting.json";
const Storage = require("Storage"); const Storage = require("Storage");
const weather = require('weather');
// ...and overwrite them with any saved values // ...and overwrite them with any saved values
@ -145,6 +146,14 @@ function printData(key, y, c){
text = "VREF"; text = "VREF";
value = E.getAnalogVRef().toFixed(2) + "V"; value = E.getAnalogVRef().toFixed(2) + "V";
} else if (key == "Weather"){
text = "TEMP";
const w = weather.get();
if (!w) {
value = "ERR";
} else {
value = require('locale').temp(w.temp-273.15); // applies conversion
}
} }
g.setColor(c); g.setColor(c);

View File

@ -18,14 +18,14 @@
storage.write(SETTINGS_FILE, settings) storage.write(SETTINGS_FILE, settings)
} }
var data_options = ["Battery", "Steps", "Temp.", "HRM", "VREF"]; var data_options = ["Battery", "Steps", "Temp.", "HRM", "VREF", "Weather"];
E.showMenu({ E.showMenu({
'': { 'title': 'LCARS Clock' }, '': { 'title': 'LCARS Clock' },
'< Back': back, '< Back': back,
'Row 1': { 'Row 1': {
value: 0 | data_options.indexOf(settings.dataRow1), value: 0 | data_options.indexOf(settings.dataRow1),
min: 0, max: 4, min: 0, max: 5,
format: v => data_options[v], format: v => data_options[v],
onchange: v => { onchange: v => {
settings.dataRow1 = data_options[v]; settings.dataRow1 = data_options[v];
@ -34,7 +34,7 @@
}, },
'Row 2': { 'Row 2': {
value: 0 | data_options.indexOf(settings.dataRow2), value: 0 | data_options.indexOf(settings.dataRow2),
min: 0, max: 4, min: 0, max: 5,
format: v => data_options[v], format: v => data_options[v],
onchange: v => { onchange: v => {
settings.dataRow2 = data_options[v]; settings.dataRow2 = data_options[v];
@ -43,7 +43,7 @@
}, },
'Row 3': { 'Row 3': {
value: 0 | data_options.indexOf(settings.dataRow3), value: 0 | data_options.indexOf(settings.dataRow3),
min: 0, max: 4, min: 0, max: 5,
format: v => data_options[v], format: v => data_options[v],
onchange: v => { onchange: v => {
settings.dataRow3 = data_options[v]; settings.dataRow3 = data_options[v];