Merge branch 'pinq--master'
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("j0ewkBiIAxHIQMJiBJEIxAaCAIQfHDgIUFDwwNCHYgVFiAVBHYgIDEghKCCIQGCFYoaDAYgORGIJ2DBwYIBHgQOPgAOIPIYOGAgQOFFgh7DHZQeDBwhoFQgh3JEAgOFFoqkHYRzgOfx4bCJ4gNGSIaJEABA7EAGA"))
|
||||
require("heatshrink").decompress(atob("mEwwMB/4AFgYCB4H//kAAoMAn/w+IFBx8P8fjAoPH4/n4/gg/j8/Px4rB+Pz58ch/wnHzz0wv/+hl5zlhDoOGnOY44FB8cZyOP/1/+OJwcfAoP44OGn4FB/lh5giBAIMz7n/AoP/nf4Aocf/IFDz5YBAoWP+YFD54FFMgIFD84FD84FM/0AApKfDApiaCAAJBCApKyCWgRlBAAWfOIIACj/8Aoc//g/BJ4KTBn4FBBIUfAoIbCx4CBFoUHAQPgDIMhAoOEV4NwVgMOn/4/jdBn8fDILpBUIfwh5TBIAYABA="))
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
0.01: New face :)
|
||||
0.02: code improvements
|
||||
0.03: code improvments to queuedraw and draw
|
|
@ -0,0 +1,18 @@
|
|||
# Encouragement & Positivity Clock
|
||||
|
||||
Tap on the watch for a note of encouragement
|
||||
|
||||
## Features
|
||||
|
||||
Pretty backgrounds
|
||||
|
||||
<img width="181" alt="Screenshot 2023-03-28-2" src="https://user-images.githubusercontent.com/44651387/228306964-be0b1d46-aee7-4562-9953-4e461954d41b.png">
|
||||
<img width="181" alt="Screenshot 2023-03-28-1" src="https://user-images.githubusercontent.com/44651387/228306967-768e0315-8652-4043-9c98-ebf0c72fd2a4.png">
|
||||
|
||||
## Requests
|
||||
|
||||
If you have any issues or would like to suggest an encouraging note, please tweet me!
|
||||
|
||||
## Creator
|
||||
|
||||
[Eleanor Tayam](http://twitter.com/elykittytee)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwMB/4AFgYCB4H//kAAoMAn/w+IFBx8P8fjAoPH4/n4/gg/j8/Px4rB+Pz58ch/wnHzz0wv/+hl5zlhDoOGnOY44FB8cZyOP/1/+OJwcfAoP44OGn4FB/lh5giBAIMz7n/AoP/nf4Aocf/IFDz5YBAoWP+YFD54FFMgIFD84FD84FM/0AApKfDApiaCAAJBCApKyCWgRlBAAWfOIIACj/8Aoc//g/BJ4KTBn4FBBIUfAoIbCx4CBFoUHAQPgDIMhAoOEV4NwVgMOn/4/jdBn8fDILpBUIfwh5TBIAYABA="))
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "encourageclk",
|
||||
"name": "Encouragement & Positivity Clock",
|
||||
"shortName":"Encouragement Clock",
|
||||
"version": "0.03",
|
||||
"description": "Tap on the watch for a note of encouragement",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme":"README.md",
|
||||
"storage": [
|
||||
{"name":"encourageclk.app.js","url":"app.js"},
|
||||
{"name":"encourageclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -46,7 +46,6 @@
|
|||
|
||||
var $name = document.getElementById('add_product_name')
|
||||
var $form = document.getElementById('add_product_form')
|
||||
var $button = document.getElementById('add_product_button')
|
||||
var $quantity = document.getElementById('add_product_quantity')
|
||||
var $list = document.getElementById('products')
|
||||
var $reset = document.getElementById('reset')
|
||||
|
@ -68,7 +67,8 @@
|
|||
ok: false
|
||||
})
|
||||
|
||||
renderProducts()
|
||||
renderProducts();
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
$name.value = ''
|
||||
$quantity.value = 1
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,17 @@
|
|||
# Long Press, Buzz!
|
||||
|
||||
Buzz at boot after a long press to indicate the watch was reinitiated at your command.
|
||||
|
||||
To infinity and beyond, space ranger!
|
||||
|
||||
## Usage
|
||||
|
||||
Just install and it will run as boot code.
|
||||
|
||||
## Requests
|
||||
|
||||
Mention @[thyttan](https://github.com/thyttan) in an issue to the official [BangleApps repository](https://github.com/espruino/BangleApps/issues) for feature requests and bug reports.
|
||||
|
||||
## Creators
|
||||
|
||||
[thyttan](https://github.com/thyttan) and [Gordon Williams](https://github.com/gfwilliams).
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1 @@
|
|||
if (BTN.read()) Bangle.buzz(80,0.40);
|
|
@ -0,0 +1,14 @@
|
|||
{ "id": "longpressbuzz",
|
||||
"name": "Long Press, Buzz!",
|
||||
"shortName":"LPB",
|
||||
"version":"0.01",
|
||||
"description": "Buzz at boot after a long press to indicate the watch was reinitiated at your command.",
|
||||
"icon": "app.png",
|
||||
"type": "bootloader",
|
||||
"tags": "system",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"longpressbuzz.0.boot.js","url":"boot.js"}
|
||||
]
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
0.01: First rev. Could use a little optimization love.
|
||||
0.02: Added battery ring, bubble background for numbers, settings and little optimaztion love.
|
|
@ -1,11 +1,16 @@
|
|||
# Rings watchface
|
||||
|
||||
Ring based watchface, read from the outside in. When the watch is unlocked the circles shrink to show the date ring.
|
||||
|
||||
By Amos Blanton, inspired by and remixed from Rinkulainen by Jukio Kallio.
|
||||
Contributors: Amos Blanton, pinq-. Inspired by Rinkulainen by Julio Kallio.
|
||||
|
||||

|
||||
View when watch is locked.
|
||||
|
||||

|
||||
Watch unlocked, showing the date.
|
||||

|
||||
View when watch is locked with numbers and bubble settings on
|
||||
|
||||

|
||||
Watch unlocked, showing the date.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,20 +2,30 @@
|
|||
// for Bangle.js 2
|
||||
// by Amos Blanton
|
||||
// Remixed from / inspired by Rinkulainen watch face by Jukio Kallio
|
||||
// 2023: pinq- added new futures
|
||||
|
||||
// To Do:
|
||||
// Make Month / year text buffer 1/2 size
|
||||
// Optimize text positioning transforms
|
||||
|
||||
const watch = {
|
||||
x:0, y:0, w:0, h:0,
|
||||
const watch = {
|
||||
color:"#000000",
|
||||
dateRing : { size:109, weight:20, color:"#00FF00", cursor:14, numbers: true },
|
||||
hourRing : { size:85, weight:20, color:"#00FFFF", cursor:14, numbers: true },
|
||||
minuteRing : { size:61, weight:20, color:"#FFFF00", cursor:14, numbers: true },
|
||||
screen : { width:g.getWidth(), height:g.getHeight(), centerX: g.getWidth() *0.5, centerY: g.getHeight() * 0.5, cursor: 14, font:"6x8:2" },
|
||||
dateRing : { size:109, weight:20, color:"#00FF00", numbers: false, range: 30 },
|
||||
hourRing : { size:82, weight:20, color:"#00FFFF", numbers: false, range: 12},
|
||||
minuteRing : { size:55, weight:18, color:"#FFFF00", numbers: false, range: 60},
|
||||
batteryRing: { size :30, weight:10, color:"#ff3300", numbers: false, range: 100},
|
||||
screen : { width:g.getWidth(), height:g.getHeight(), centerX: g.getWidth() *0.5, centerY: g.getHeight() * 0.5, cursor: 14, font:"Vector:18", bubble:false },
|
||||
};
|
||||
|
||||
var settings = require('Storage').readJSON("rings.settings.json", true) || {};
|
||||
|
||||
if(settings.minute){
|
||||
watch.minuteRing.numbers = settings.minute.numbers;
|
||||
watch.hourRing.numbers = settings.hour.numbers;
|
||||
watch.dateRing.numbers = settings.date.numbers;
|
||||
watch.screen.bubble = settings.bubble;
|
||||
}
|
||||
delete settings;
|
||||
const month= ["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY",
|
||||
"AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"];
|
||||
|
||||
|
@ -45,31 +55,77 @@ function queueDraw() {
|
|||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
// Draws a time circle (date, hours, minutes)
|
||||
function drawTimeCircle(color, size, weight, range, value ) {
|
||||
// Draws a circles (date, hours, minutes)
|
||||
function drawCircle(ringValues, offset, value ) {
|
||||
// variables for vertex transformations and positioning time
|
||||
var tver, tobj, tran;
|
||||
var ttime = (value / range) * (Math.PI * 2);
|
||||
let tver, tobj, tran;
|
||||
let ttime = (value / ringValues.range) * (Math.PI * 2);
|
||||
|
||||
// draw circle and line
|
||||
g.setColor(color).fillCircle(watch.screen.centerX, watch.screen.centerY, size);
|
||||
g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, size - weight);
|
||||
|
||||
tver = [-watch.screen.cursor, 0, watch.screen.cursor, 0, watch.screen.cursor, -size*1.01, -watch.screen.cursor, -size*1.05];
|
||||
// draw circle
|
||||
g.setColor(ringValues.color).fillCircle(watch.screen.centerX, watch.screen.centerY, ringValues.size + offset);
|
||||
g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, ringValues.size - ringValues.weight + offset);
|
||||
|
||||
tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate:ttime };
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.fillPoly(tran);
|
||||
|
||||
if(watch.screen.bubble){
|
||||
tver = [-1, 0, 1, 0, 1, -ringValues.size-offset, -1, -(ringValues.size + offset -21)];
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
if(ringValues.numbers){
|
||||
g.setColor("#000000").fillCircle((tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2, 17 + offset/10);
|
||||
}else{
|
||||
g.setColor("#000000").fillCircle((tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2, 10 + offset/10);
|
||||
}
|
||||
}else{
|
||||
if(ringValues.numbers){
|
||||
tver = [-watch.screen.cursor, 0, watch.screen.cursor, 0, watch.screen.cursor, -ringValues.size*1.01 - offset, -watch.screen.cursor, -ringValues.size*1.05 - offset];
|
||||
}else{
|
||||
tver = [-watch.screen.cursor * 0.4, 0, watch.screen.cursor * 0.4, 0, watch.screen.cursor *0.4, -ringValues.size*1.01 - offset, -watch.screen.cursor*0.4, -ringValues.size*1.05 - offset];
|
||||
}
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.fillPoly(tran);
|
||||
|
||||
}
|
||||
|
||||
// Draw numbers
|
||||
g.setFontAlign(0,0).setFont(watch.screen.font, 2).setColor(1,1,1);
|
||||
|
||||
if(ringValues.numbers){
|
||||
// size - 21 is the right offset to get the numbers aligned in the circle.
|
||||
tver = [-1, 0, 1, 0, 1, -size, -1, -(size -21)];
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(value, (tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2 );
|
||||
tver = [-1, 0, 1, 0, 1, -ringValues.size-offset, -1, -(ringValues.size + offset -21)];
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
//g.setColor(1,1,1);
|
||||
g.setFontAlign(0,0).setFont(watch.screen.font, 2).setColor(1,1,1);
|
||||
g.drawString(value, (tran[4]+tran[6]) / 2 , (tran[5]+tran[7]) / 2 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// For battery disable
|
||||
function drawArc(percent, color, ArchR) {
|
||||
let offset = 0;
|
||||
let end = 360;
|
||||
let radius = ArchR + 2;
|
||||
|
||||
if (percent <= 0) return; // no gauge needed
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
let startRotation = -offset;
|
||||
let endRotation = startRotation - (end * percent);
|
||||
|
||||
g.setColor(color);
|
||||
// convert to radians
|
||||
startRotation *= Math.PI / 180;
|
||||
let amt = Math.PI / 10;
|
||||
endRotation = (endRotation * Math.PI / 180) - amt;
|
||||
// all we need to draw is an arc, because we'll fill the center
|
||||
let poly = [watch.screen.centerX, watch.screen.centerY];
|
||||
for (let r = startRotation; r > endRotation; r -= amt)
|
||||
poly.push(
|
||||
watch.screen.centerX - radius * Math.sin(r),
|
||||
watch.screen.centerY - radius * Math.cos(r)
|
||||
);
|
||||
g.fillPoly(poly);
|
||||
g.setColor("#000000").fillCircle(watch.screen.centerX, watch.screen.centerY, ArchR - 10);
|
||||
g.setColor(color).fillCircle(watch.screen.centerX - (radius -5) * Math.sin(endRotation + amt), watch.screen.centerY - (radius -5) * Math.cos(endRotation + amt), 4);
|
||||
}
|
||||
|
||||
// Draws text for month and year in date circle
|
||||
|
@ -80,16 +136,16 @@ function drawMonthCircleText( text, circleSize, range, value){
|
|||
|
||||
monthCircleTextBuffer.clear();
|
||||
monthCircleTextBuffer.fillRect(0,0,watch.screen.width,watch.screen.height);
|
||||
|
||||
var tver, tobj, tran;
|
||||
|
||||
|
||||
// From here: https://forum.espruino.com/comments/16781795/
|
||||
var gr = Graphics.createArrayBuffer(24,16,1,{msb:true});
|
||||
var grimg = gr.asImage();
|
||||
grimg.transparent = 1;
|
||||
monthCircleTextBuffer.setColor(0,0,0);
|
||||
monthCircleTextBuffer.setColor(1,1,1);
|
||||
|
||||
for(z=0; z < text.length; z++){
|
||||
for(z=0; z < text.length; z++){
|
||||
tobj = { x:watch.screen.centerX, y:watch.screen.centerY, scale:1, rotate: ((z + 1) / range) * (Math.PI * 2) };
|
||||
tver = [-1, 0, 1, 0, 1, -circleSize, -1, -(circleSize -21)];
|
||||
tran = monthCircleTextBuffer.transformVertices(tver, tobj);
|
||||
|
@ -119,7 +175,6 @@ function drawMonthCircleText( text, circleSize, range, value){
|
|||
function shrinkCircles(toggle){
|
||||
// If there's a queued draw operation,removeit so animation isn't interrupted.
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
|
||||
var date = new Date();
|
||||
var delta = 1;
|
||||
|
||||
|
@ -128,6 +183,7 @@ function shrinkCircles(toggle){
|
|||
counter = 1;
|
||||
// We're finished, so queue next draw.
|
||||
queueDraw();
|
||||
if(!toggle) drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -142,14 +198,14 @@ function shrinkCircles(toggle){
|
|||
|
||||
// Draw the date ring (unless it's the last run of an expansion).
|
||||
if(counter < 11 || toggle){
|
||||
drawTimeCircle(watch.dateRing.color, watch.dateRing.size + delta, watch.dateRing.weight, getDays(date.getFullYear(), date.getMonth()+1), date.getDate() );
|
||||
|
||||
drawCircle(watch.dateRing, delta, date.getDate());
|
||||
// Draw month and year in date ring
|
||||
drawMonthCircleText( month[date.getMonth()]+" "+date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ;
|
||||
drawMonthCircleText( date.getDate() + " " + month[date.getMonth()] + " " +date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ;
|
||||
}
|
||||
|
||||
drawTimeCircle(watch.hourRing.color, watch.hourRing.size + delta, watch.hourRing.weight, 12, date.getHours() );
|
||||
|
||||
drawTimeCircle(watch.minuteRing.color, watch.minuteRing.size + delta, watch.minuteRing.weight, 60, date.getMinutes() );
|
||||
drawCircle(watch.hourRing, delta, date.getHours());
|
||||
drawCircle(watch.minuteRing, delta, date.getMinutes());
|
||||
|
||||
counter += 1;
|
||||
setTimeout(shrinkCircles, 10, toggle);
|
||||
|
@ -170,19 +226,23 @@ function draw() {
|
|||
g.fillRect(0, 0, watch.screen.width, watch.screen.height);
|
||||
|
||||
// If unlocked, draw date ring and text and make hour and minute rings smaller
|
||||
var days_month = getDays(date.getFullYear(), date.getMonth()+1);
|
||||
if(!Bangle.isLocked()){
|
||||
unLockedOffset = 24;
|
||||
drawTimeCircle(watch.dateRing.color, watch.dateRing.size - unLockedOffset, watch.dateRing.weight, getDays(date.getFullYear(), date.getMonth()+1), date.getDate() );
|
||||
drawMonthCircleText( month[date.getMonth()]+" "+date.getFullYear(), watch.dateRing.size - unLockedOffset, getDays(date.getFullYear(), date.getMonth()+1), date.getDate()) ;
|
||||
// if the day has changed
|
||||
if(watch.dateRing.range != days_month) watch.dateRing.range = days_month;
|
||||
drawCircle(watch.dateRing, -unLockedOffset, date.getDate());
|
||||
drawMonthCircleText( date.getDate() + " " + month[date.getMonth()] + " " + date.getFullYear(), watch.dateRing.size - 24, getDays(date.getFullYear(), date.getMonth()+1), date.getDate());
|
||||
}
|
||||
drawCircle(watch.hourRing, -unLockedOffset, date.getHours());
|
||||
drawCircle(watch.minuteRing, -unLockedOffset, date.getMinutes());
|
||||
if(Bangle.isLocked()){
|
||||
drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size);
|
||||
}
|
||||
|
||||
drawTimeCircle(watch.hourRing.color, watch.hourRing.size - unLockedOffset, watch.hourRing.weight, 12, date.getHours() );
|
||||
drawTimeCircle(watch.minuteRing.color, watch.minuteRing.size -unLockedOffset , watch.minuteRing.weight, 60, date.getMinutes() );
|
||||
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
// Trigger shrink / expand animation on unlock / lock events
|
||||
//drawArc(E.getBattery() / 100, watch.batteryRing.color, watch.batteryRing.size); Trigger shrink / expand animation on unlock / lock events
|
||||
Bangle.on('lock', on=>{
|
||||
if (on) { // locked, expand circles
|
||||
counter = 1;
|
||||
|
@ -204,7 +264,7 @@ g.clear();
|
|||
draw();
|
||||
|
||||
|
||||
// console.log("Whatevs");
|
||||
// .log("Whatevs");
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
|
@ -1,16 +1,36 @@
|
|||
{ "id": "rings",
|
||||
{
|
||||
"id": "rings",
|
||||
"name": "Rings - an animated watchface",
|
||||
"shortName":"Rings",
|
||||
"version":"0.01",
|
||||
"shortName": "Rings",
|
||||
"version": "0.02",
|
||||
"description": "Ring based watchface that animates to show the date when unlocked. Inspired by / remixed from Rinkulainen.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}, {"url":"screenshot2.png"}],
|
||||
"screenshots": [{
|
||||
"url": "screenshot1.png"
|
||||
}, {
|
||||
"url": "screenshot2.png"
|
||||
}, {
|
||||
"url": "screenshot3.png"
|
||||
}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"rings.app.js","url":"app.js"},
|
||||
{"name":"rings.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
"storage": [{
|
||||
"name": "rings.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "rings.img",
|
||||
"url": "app-icon.js",
|
||||
"evaluate": true
|
||||
},
|
||||
{
|
||||
"name": "rings.settings.js",
|
||||
"url": "settings.js"
|
||||
}
|
||||
],
|
||||
"data": [{
|
||||
"name": "rings.settings.json"
|
||||
}]
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,68 @@
|
|||
(function(back) {
|
||||
var FILE = "rings.settings.json";
|
||||
// Load settings
|
||||
var settings = Object.assign({
|
||||
minute: {
|
||||
numbers: false,
|
||||
},
|
||||
hour: {
|
||||
numbers: false
|
||||
},
|
||||
date: {
|
||||
numbers: false,
|
||||
},
|
||||
bubble: false,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings(key, value) {
|
||||
settings[key] = value;
|
||||
require("Storage").write("rings.settings.json",settings);
|
||||
}
|
||||
var mainmenu = {
|
||||
"" : { "title" : "Rings" },
|
||||
"< Back" : () => back(),
|
||||
/*LANG*/"Minute" : () => E.showMenu({
|
||||
"" : { "title" : /*LANG*/"Minute" },
|
||||
"< Back" : ()=>E.showMenu(mainmenu),
|
||||
/*LANG*/"Number" : {
|
||||
value: settings.minute.numbers,
|
||||
onchange: (m) => {
|
||||
settings.minute.numbers = m,
|
||||
writeSettings();
|
||||
},
|
||||
}
|
||||
}),
|
||||
/*LANG*/"Hour" : () => E.showMenu({
|
||||
"" : { "title" : /*LANG*/"Hour" },
|
||||
"< Back" : ()=>E.showMenu(mainmenu),
|
||||
/*LANG*/"Number" : {
|
||||
value: settings.hour.numbers,
|
||||
onchange: (m) => {
|
||||
settings.hour.numbers = m,
|
||||
writeSettings();
|
||||
},
|
||||
}
|
||||
}),
|
||||
/*LANG*/"Date" : () => E.showMenu({
|
||||
"" : { "title" : /*LANG*/"Date" },
|
||||
"< Back" : ()=>E.showMenu(mainmenu),
|
||||
/*LANG*/"Number" : {
|
||||
value: settings.date.numbers,
|
||||
onchange: (m) => {
|
||||
settings.date.numbers = m,
|
||||
writeSettings();
|
||||
},
|
||||
}
|
||||
}),
|
||||
/*LANG*/"BG Bubble?" : {
|
||||
value : settings.bubble,
|
||||
onchange: (m) => {
|
||||
settings.bubble = m;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu(mainmenu);
|
||||
})
|
|
@ -1,2 +1,4 @@
|
|||
0.01: Inital release.
|
||||
0.02: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/2d3c34ef7c2b9fe2118e816aacd2e096adb99596).
|
||||
0.03: Rebasing on latest changes to showScroller_Q3 (https://github.com/espruino/Espruino/commit/b6f8105b6348bb6f7cd03ac11efc1f3585c6ad79). Ensure that changing a menu item when half-scrolled off screen doesn't overwrite widgets.
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ var s = {
|
|||
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
|
||||
}, drawItem : i => {
|
||||
var y = idxToY(i);
|
||||
g.reset().setClipRect(R.x,y,R.x2,y+options.h);
|
||||
g.reset().setClipRect(R.x,Math.max(y,R.y),R.x2,Math.min(y+options.h,R.y2));
|
||||
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});
|
||||
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
|
||||
}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "swscroll",
|
||||
"name": "Swipe menus",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Replace built in E.showScroller to act on swipe instead of drag. Navigate menus in discrete steps instead of a continuous motion.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App
|
||||
0.02: Set API Key through Apploader
|
|
@ -0,0 +1,7 @@
|
|||
# Tiny Coinmarketcap
|
||||
A simple app using Gadgedtbridge internet access to fetch current crypto prices from Coinmarketcap.
|
||||
I'm providing a "free-tier" API Key.
|
||||
|
||||
TO-DOs:
|
||||
- [X] Add own API Key option
|
||||
- [ ] Add settings for Currency conversion
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4kA/4ACBIP/o8242462/k8/gtytlz1s+r2/gv/q9Zpv/m+02pj/AH4AqgMRBhURAAQWGqPxqPwA4UoloABkoLCAIQhIiMQgUo29mtfr6WIBogxDFwNBAIeKwd8x2Mud8lUhBolQLoYABrERjurs/S7F329rs+i+I+EC4JRFxl+lUmvmGAIWKkJhGJ4lI01y2+3s4BB1s7s+o+MVMAhPEll7kd+uYBCvgBB7khyIRCL4cZiMS7YuCAAxnBSAsBJoWKl0sLYYBFluBL4iPCiuNsRdEAIlr3GBR4iQBJoMs7pdEAIjDBkK/ESAUY665BABNn7AvFkWBlEsXYoBFMAMlkPwC4UYRwOru5ZDMY+iitBF4MoPAOBxsjXoktAYMoYYcowOBwEokAXBiq+BL4eywWH2WHA4VrYAMViECgEgYAOIxhXDlF8k0oM4eDxrABkReBkEIj/y1ZVCsRjB2UnL4tYimCO4UBoOdlhdCveDAYMuYId8kq/DGIOI/Hyw6MDAA9t7FI/HwJAUBqOBll7X5WK+PxqK/DgMRimNsS7HAINm3DuBd4hfB+ONxhZEAIl8lVBL4YXCjLwB7eyLxFn7AvHJ4PxlS7BLo2OvmBqJfHAANC02yLotrs+iiMYB4IWCL4QgByMsvlzXYlz7UhLoIBBC4cADwMViNS7drFQNnAYXSFwZeDAAJPCyPxlEsFgIuClouBBoPxRwZgEGAMYwWI81n9sikA9CFwwwCKIWRyOFlEtlANEoAWGMIYyDisVCBAAHKYRjBqOAC6AyECqQA/AH4AJA=="))
|
|
@ -0,0 +1,118 @@
|
|||
let settings = require("Storage").readJSON("tinycmc.json", 1);
|
||||
const apiKey = settings.apikey || null;
|
||||
const cmcBase = 'https://pro-api.coinmarketcap.com/';
|
||||
const version = ['v1', 'v2'];
|
||||
const path = {
|
||||
latest: '/cryptocurrency/listings/latest',
|
||||
quote: '/cryptocurrency/quotes/latest'
|
||||
};
|
||||
let page = 0;
|
||||
function displayLatest(offset) {
|
||||
g.clear();
|
||||
if (page === 0) {
|
||||
E.showMessage('Getting Top 10');
|
||||
} else {
|
||||
E.showMessage(`Getting Top ${offset} to ${offset + 9}`);
|
||||
}
|
||||
|
||||
const uri = offset ? `${cmcBase}${version[0]}${path.latest}?convert=EUR&limit=10&start=${offset}` : `${cmcBase}${version[0]}${path.latest}?convert=EUR&limit=10`;
|
||||
Bangle.http(uri,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-CMC_PRO_API_KEY": apiKey,
|
||||
}
|
||||
}).then(data=>{
|
||||
const result = JSON.parse(data.resp).data;
|
||||
let menu = {
|
||||
"" : { title : "-- Select --" },
|
||||
};
|
||||
//FIXME: Menu can also take an array of items, this would be easier to compose using map
|
||||
result.forEach(listing => {
|
||||
menu[listing.name] = {
|
||||
title: `${listing.cmc_rank}: ${listing.symbol} ${Number(listing.quote.EUR.percent_change_24h).toFixed(3)}`,
|
||||
onchange: function() { E.showMenu(); displayQuote(listing.symbol);} };
|
||||
});
|
||||
menu.Next = function() {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
page = page + 1;
|
||||
displayLatest((page * 10) + 1);
|
||||
}; // remove the menu
|
||||
menu.Exit = function() {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
Bangle.showClock();
|
||||
}; // remove the menu
|
||||
g.clear();
|
||||
E.showMenu(menu);
|
||||
setWatch(() => {
|
||||
g.clear();
|
||||
displayMenu();
|
||||
}, BTN, {edge:"rising", debounce:50, repeat:true});
|
||||
}).catch(error=>{
|
||||
console.log('Error');
|
||||
console.log(error);
|
||||
E.showMessage(`${error}\nTo go back press BTN`);
|
||||
setWatch(() => {
|
||||
g.clear();
|
||||
displayMenu();
|
||||
}, BTN, {edge:"rising", debounce:50, repeat:true});
|
||||
});
|
||||
}
|
||||
|
||||
function displayQuote(symb) {
|
||||
g.clear();
|
||||
E.showMessage(`Getting latest for ${symb}`);
|
||||
Bangle.http(`${cmcBase}${version[1]}${path.quote}?symbol=${symb}&convert=EUR`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-CMC_PRO_API_KEY": apiKey,
|
||||
}
|
||||
}).then(data=>{
|
||||
g.clear();
|
||||
const result = JSON.parse(data.resp).data[symb][0];
|
||||
E.showMessage(`#${result.cmc_rank}: ${result.symbol}\n${Number(result.quote.EUR.price).toFixed(2)}\n%24h:
|
||||
${result.quote.EUR.percent_change_24h}`);
|
||||
setWatch(() => {
|
||||
g.clear();
|
||||
displayMenu();
|
||||
}, BTN, {edge:"rising", debounce:50, repeat:true});
|
||||
}).catch(error=>{
|
||||
E.showMessage(`${error}\nTo go back press BTN`);
|
||||
setWatch(() => {
|
||||
g.clear();
|
||||
displayMenu();
|
||||
}, BTN, {edge:"rising", debounce:50, repeat:true});
|
||||
});
|
||||
}
|
||||
|
||||
function displayMenu() {
|
||||
if (!apiKey) {
|
||||
E.showMessage("Please provide a Coinmarketcap API Key");
|
||||
} else {
|
||||
// Actually display the menu
|
||||
E.showMenu({
|
||||
"" : { title : "-- Select --" }, // options
|
||||
"Latest": function() { E.showMenu(); displayLatest(); },
|
||||
"BTC" : function() { E.showMenu(); displayQuote('BTC'); },
|
||||
"ETH" : function() { E.showMenu(); displayQuote('ETH'); },
|
||||
"XMR" : function() { E.showMenu(); displayQuote('XMR'); },
|
||||
"ADA" : function() { E.showMenu(); displayQuote('ADA'); },
|
||||
"DOGE" : function() { E.showMenu(); displayQuote('DOGE'); },
|
||||
"LTC" : function() { E.showMenu(); displayQuote('LTC'); },
|
||||
"Exit" : function() {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
Bangle.showClock();
|
||||
}, // remove the menu
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function main () {
|
||||
displayMenu();
|
||||
}
|
||||
|
||||
main ();
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,63 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<h3>Set Coinmarketcap API key</h3>
|
||||
<p><input id="apikey" onkeyup="checkInput()" style="width:90%; margin: 3px"></input><button id="upload" class="btn btn-primary">Save key</button></p>
|
||||
|
||||
<h4>Where to get your personal API key?</h4>
|
||||
<p>Go to <a href="https://coinmarketcap.com/api/">https://coinmarketcap.com/api/</a> and sign up for a free account.<br>
|
||||
After registration you can login and obtain your personal API key.</p>
|
||||
|
||||
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
function checkInput() {
|
||||
if(document.getElementById("apikey").value==="") {
|
||||
document.getElementById('upload').disabled = true;
|
||||
} else {
|
||||
document.getElementById('upload').disabled = false;
|
||||
}
|
||||
}
|
||||
checkInput();
|
||||
|
||||
var settings = {};
|
||||
function onInit(){
|
||||
console.log("Loading settings from BangleJs...");
|
||||
try {
|
||||
Util.readStorage("tinycmc.json", data=>{
|
||||
if(data.length > 0){
|
||||
settings = JSON.parse(data);
|
||||
console.log("Got settings", settings);
|
||||
document.getElementById("apikey").value = settings.apikey;
|
||||
console.log("Loaded apikey from BangleJs.");
|
||||
checkInput();
|
||||
}
|
||||
});
|
||||
} catch(ex) {
|
||||
console.log("(Warning) Could not load apikey from BangleJs.");
|
||||
console.log(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
try {
|
||||
settings.apikey = document.getElementById("apikey").value;
|
||||
Util.showModal("Saving...");
|
||||
Util.writeStorage("tinycmc.json", JSON.stringify(settings), ()=>{
|
||||
Util.hideModal();
|
||||
});
|
||||
console.log("Sent settings!");
|
||||
} catch(ex) {
|
||||
console.log("(Warning) Could not write settings to BangleJs.");
|
||||
console.log(ex);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"id": "tinycmc",
|
||||
"name": "Tiny CMC",
|
||||
"shortName":"tinycmc",
|
||||
"icon": "icon.png",
|
||||
"version": "0.02",
|
||||
"description": "TinyCMC is a bangle.js Coinmarketcap API client",
|
||||
"type": "app",
|
||||
"tags": "tools",
|
||||
"supports": [
|
||||
"BANGLEJS2"
|
||||
],
|
||||
"allow_emulator": false,
|
||||
"interface": "interface.html",
|
||||
"readme": "README.md",
|
||||
"data": [
|
||||
{"name":"tinycmc.json"}
|
||||
],
|
||||
"storage": [
|
||||
{
|
||||
"name": "tinycmc.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "tinycmc.img",
|
||||
"url": "app-icon.js",
|
||||
"evaluate": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
0.01: New widget
|
||||
0.02: Make color depend on level
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widbatv",
|
||||
"name": "Battery Level Widget (Vertical)",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Slim, vertical battery widget that only takes up 14px",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
|
|
@ -14,6 +14,10 @@ WIDGETS["batv"]={area:"tr",width:14,draw:function() {
|
|||
} else {
|
||||
g.clearRect(x,y,x+14,y+24);
|
||||
g.setColor(g.theme.fg).fillRect(x+2,y+2,x+12,y+22).clearRect(x+4,y+4,x+10,y+20).fillRect(x+5,y+1,x+9,y+2);
|
||||
g.setColor("#0f0").fillRect(x+4,y+20-(E.getBattery()*16/100),x+10,y+20);
|
||||
var battery = E.getBattery();
|
||||
if (battery < 20) {g.setColor("#f00");}
|
||||
else if (battery < 40) {g.setColor(g.theme.dark ? "#ff0" : "#f80");}
|
||||
else {g.setColor("#0f0");}
|
||||
g.fillRect(x+4,y+20-(E.getBattery()*16/100),x+10,y+20);
|
||||
}
|
||||
}};
|
||||
|
|