Merge branch 'espruino:master' into rescalc

master
stweedo 2023-05-18 12:09:29 -05:00 committed by GitHub
commit c1f103eea7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 390 additions and 103 deletions

View File

@ -6,3 +6,4 @@
0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc.
0.07: Use clock_info.addInteractive instead of a custom implementation
0.08: Use clock_info module as an app
0.09: clock_info now uses app name to maintain settings specifically for this clock face

View File

@ -193,6 +193,7 @@ function queueDraw() {
*/
let clockInfoItems = clock_info.load();
let clockInfoMenu = clock_info.addInteractive(clockInfoItems, {
app : "aiclock",
x : 0,
y: 0,
w: W,

View File

@ -3,7 +3,7 @@
"name": "AI Clock",
"shortName":"AI Clock",
"icon": "aiclock.png",
"version":"0.08",
"version":"0.09",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"dependencies" : { "clock_info":"module" },

View File

@ -3,6 +3,7 @@
"version":"0.01",
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays the day of the month in the icon, and the weekday",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "clkinfo",
"tags": "clkinfo,calendar",
"supports" : ["BANGLEJS2"],

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1 +1,3 @@
0.01: New app!
0.01: New app!
0.02: Add sensor icons
Customize code directly, remove config file

View File

@ -1,5 +1,5 @@
(function () {
const sb = () => require('hasensors').sendBattery();
const sb = () => require("hasensors").sendBattery();
Bangle.on("charging", sb);
NRF.on("connect", () => setTimeout(sb, 2000));
setInterval(sb, 10 * 60 * 1000);

View File

@ -39,14 +39,27 @@
<a href="https://my.home-assistant.io/redirect/profile/" target="_blank">your user profile</a>.</span></label>
</form>
<p>
<button id="upload" class="btn btn-primary">Upload</button>
<button id="upload" class="btn btn-primary" disabled>Upload</button>
</p>
<script src="../../core/lib/customize.js"></script>
<script>
const STORAGE_KEY = 'hasensors-config';
const fields = ['id', 'name', 'url', 'token'];
const form = document.getElementById('sensorform');
const STORAGE_KEY = "hasensors-config";
const fields = ["id", "name", "url", "token"];
const form = document.getElementById("sensorform");
const LIBRARY_URL = "./lib.js";
// fetch library code template, enable upload button once we"ve got it
let libTpl;
fetch(LIBRARY_URL).then(response=>{
if (! response.ok) return;
console.log(response);
response.text().then(code=>{
libTpl = code;
document.getElementById("upload").disabled = false;
});
});
// try to pre-fill form with values previously saved in localStorage
let stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
@ -62,7 +75,7 @@
}
document.getElementById("upload").addEventListener("click", function () {
let config = {};
// validate form fields or bail out
for (const field of fields) {
if (!form[field].validity.valid) {
form[field].focus();
@ -70,18 +83,21 @@
return;
}
}
let config = {};
for (const field of fields) {
config[field] = form[field].value
config[field] = form[field].value;
}
console.log('config:', config, JSON.stringify(config));
// save config to localStorage for re-use next time
localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
// replace {placeholders} in library code template
const lib = libTpl.replace(/\{(\w+)\}/g, (_,f) => config[f]);
console.log("config:", config, JSON.stringify(config));
sendCustomizedApp({
id: "hasensors",
storage: [
{name: "hasensors.boot.js", url: "boot.js"},
{name: "hasensors", url: "lib.js"},
{name: "hasensors.settings.json", content: JSON.stringify(config)},
]
{name: "hasensors", content: lib},
],
});
});
</script>

View File

@ -1,35 +1,43 @@
// split out into a separate file to keep bootcode short.
function s(key) {
return (require('Storage').readJSON('hasensors.settings.js', true) || {})[key];
}
// placeholders are replaced by custom.html before upload
function post(sensor, data) {
const url = s('url') + '/api/states/sensor.' + s('id') + '_' + sensor;
const url = "{url}/api/states/sensor.{id}_" + sensor;
Bangle.http(url, {
method: 'POST',
method: "POST",
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + s('token'),
"Content-Type": "application/json",
Authorization: "Bearer {token}",
}
});
}
exports.sendBattery = function () {
if (!NRF.getSecurityStatus().connected) return;
post('battery_level', {
state: E.getBattery(),
const b = E.getBattery(),
c = Bangle.isCharging();
let i = "mdi:battery";
if (c) i += "-charging";
post("battery_state", {
state: c ? "charging" : "discharging",
attributes: {
friendly_name: s('name') + " Battery Level",
friendly_name: "{name} Battery State",
icon: i + (c ? "" : "-minus"),
}
});
if (b<10) i += "-outline"; // there is no battery-0
else if (b<100 || c) i += "-" + Math.floor(b/10)*10; // no battery-100 either
post("battery_level", {
state: b,
attributes: {
friendly_name: "{name} Battery Level",
unit_of_measurement: "%",
device_class: "battery",
state_class: "measurement",
}
});
post('battery_state', {
state: Bangle.isCharging() ? 'charging' : 'discharging',
attributes: {
friendly_name: s('name') + " Battery State",
icon: i,
}
});
}

View File

@ -2,7 +2,7 @@
"id": "hasensors",
"name": "Home Assistant Sensors",
"shortName": "HA sensors",
"version": "0.01",
"version": "0.02",
"description": "Send sensor values to Home Assistant using the Android Integration.",
"icon": "ha.png",
"type": "bootloader",
@ -14,8 +14,5 @@
"storage": [
{"name":"hasensors","url":"lib.js"},
{"name":"hasensors.boot.js","url":"boot.js"}
],
"data": [
{"name":"hasensors.settings.json"}
]
}

View File

@ -4,3 +4,4 @@
0.04: Show a random kana every minute to improve learning
0.05: Tell clock widgets to hide
0.06: Fix exception when showing missing hiragana 'WO'
0.07: Fix regression in bitmap selection on some code paths

View File

@ -224,6 +224,7 @@ function drawKana (x, y) {
g.setColor(0, 0, 0);
g.fillRect(0, 0, g.getWidth(), 6 * (h / 8) + 1);
g.setColor(1, 1, 1);
kana = hiramode ? hiragana[curkana] : katakana[curkana];
g.drawImage(kana, x + 20, 40, { scale: 1.6 });
g.setColor(1, 1, 1);
g.setFont('Vector', 24);
@ -266,4 +267,3 @@ Bangle.setUI('clock');
Bangle.loadWidgets();
tickWatch();
setInterval(tickWatch, 1000 * 60);

View File

@ -2,7 +2,7 @@
"id": "kanawatch",
"name": "Kanawatch",
"shortName": "Kanawatch",
"version": "0.06",
"version": "0.07",
"type": "clock",
"description": "Learn Hiragana and Katakana",
"icon": "app.png",

View File

@ -1,2 +1,3 @@
0.01: first release
0.02: Use clock_info module as an app
0.03: clock_info now uses app name to maintain settings specifically for this clock face

View File

@ -37,13 +37,13 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
{
// 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);
@ -66,7 +66,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
/**
* 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
*
@ -76,7 +76,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
//g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg);
g.reset().setFontLatoSmall();
g.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
@ -88,7 +88,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
// clear the whole info line, allow additional 2 pixels in case LatoFont overflows area
g.clearRect(0, options.y -2, g.getWidth(), options.y+ 23 + 2);
// draw the image if we have one
if (info.img) {
// image start
@ -110,8 +110,8 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
// 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"} );
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", 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();

View File

@ -37,13 +37,13 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
{
// 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);
@ -66,7 +66,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
/**
* 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
*
@ -76,7 +76,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
//g.reset().setFont('Vector',24).setBgColor(options.bg).setColor(options.fg);
g.reset().setFontLatoSmall();
g.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
@ -88,7 +88,7 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
// clear the whole info line, allow additional 2 pixels in case LatoFont overflows area
g.clearRect(0, options.y -2, g.getWidth(), options.y+ 23 + 2);
// draw the image if we have one
if (info.img) {
// image start
@ -110,8 +110,8 @@ Graphics.prototype.setFontLatoSmall = function(scale) {
// 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"} );
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", 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();

View File

@ -64,7 +64,7 @@ function queueDraw() {
/**
* 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
*
@ -109,8 +109,8 @@ let clockInfoItems = require("clock_info").load();
* 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"} );
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app : "lato", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} );
g.clear();
// Show launcher when middle button pressed

View File

@ -1,7 +1,7 @@
{
"id": "lato",
"name": "Lato",
"version": "0.02",
"version": "0.03",
"description": "A Lato Font clock with fast load and clock_info",
"readme": "README.md",
"icon": "app.png",

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Use clock_info module as an app
0.03: clock_info now uses app name to maintain settings specifically for this clock face

View File

@ -79,6 +79,6 @@ let clockInfoDraw = (itm, info, options) => {
};
let clockInfoItems = require("clock_info").load();
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { x:R.x, y:R.y, w:midX-2, h:barY-R.y-2, draw : clockInfoDraw});
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { x:midX+2, y:R.y, w:midX-3, h:barY-R.y-2, draw : clockInfoDraw});
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:R.x, y:R.y, w:midX-2, h:barY-R.y-2, draw : clockInfoDraw});
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"lcdclock", x:midX+2, y:R.y, w:midX-3, h:barY-R.y-2, draw : clockInfoDraw});
}

View File

@ -1,6 +1,6 @@
{ "id": "lcdclock",
"name": "LCD Clock",
"version":"0.02",
"version":"0.03",
"description": "A Casio-style clock, with ClockInfo areas at the top and bottom. Tap them and swipe up/down to toggle between different information",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

View File

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

View File

@ -0,0 +1,15 @@
# Loading Screen Settings
This app allows you to choose the loading screen that's displayed when swapping between apps on Bangle.js.
## Usage
Go to the Launcher, then `Settings`, then `Apps` and click on `Loading Screen` - you can then click to choose the loading screen you'll see.
## Internals
When reloading an app (fast load doesn't display anything), Bangle.js looks for a file called `.loading` (in versions 2v18+) which should be
an image. If it doesn't exist, the default `Loading...` screen is displayed. If it does exist and is less than 3 bytes long,
nothing is displayed, or if it's an image it is displayed, centered.
This app sets that image file accordingly, but you can always upload your own file.

BIN
apps/loadingscreen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

View File

@ -0,0 +1,14 @@
{ "id": "loadingscreen",
"name": "Loading Screen",
"version":"0.01",
"description": "On Bangle.js 2v18+, this lets you customize the app loading screen via an new menu in Settings -> Apps",
"icon": "app.png",
"screenshots" : [ { "url":"screenshot1.png" }, { "url":"screenshot2.png" } ],
"tags": "tool",
"type": "settings",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"loadingscreen.settings.js","url":"settings.js"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,81 @@
(function(back) {
function goBack() {
var im = require("Storage").read(".loading");
if (im && im.length>3) {
var i = g.imageMetrics(im);
g.reset().drawImage(im, (g.getWidth()-i.width)/2, (g.getHeight()-i.height)/2);
}
setTimeout(back, 500);
}
function savePattern(pattern) {
E.showMessage("Please wait...");
var im = Graphics.createImage(pattern,"string");
var w = g.getWidth(), h = g.getHeight();
var b = Graphics.createArrayBuffer(w,h,1,{msb:true});
for (var y=0;y<h;y+=im.height)
for (var x=0;x<w;x+=im.width)
b.drawImage(im,x,y);
b.transparent = 0;
require("Storage").write(".loading",b.asImage("string"));
goBack();
}
function iconHourglass() {
// To get FG+BG+transparent, convert a monochrome image as a 2 bit greyscale using the image converter
return require("heatshrink").decompress(atob("mE4wUBqAOJgtABZMBGjH/ABALlBhA8LBIJrBBZCDCBZEFqoLJqoLO4BBDgYLcn/wBYcP/gLFCYIbBBY38BYYFBBYVACIILE+EBBYY1BLgX/BYgVBEAQECBYSQCJAQcCRwNVqg8CBYIDCioLDCYQbDBYRUBFYPwBwJ8BBQQ8BCgQaCHQQLNEZQ7LKZZrLQYbKDQYabDAASbFYoQACWYg2DAQrjPNIRtCBYjKCBYLMCBaIKBAAILGAAwLNRwQLHgrJCBY8BRIQLHSoYLJBQ4MDBcYAWNYIAIgKDBABEFA="));
}
function iconRetro() {
// A mac-style loading window
return require("heatshrink").decompress(atob("rk1gP/ABP4DBMDwALJnAWK39/+4tH3EEgIWJoEXC34WZORSghZ1oW/CzkB8AQC4AWFhgEBgOMFowSCjAtGhkPx8HzncuwWE4OMjHY41sDgQWCjHPzOMs3MCwuNzOYCw9Y7IWI5oWE5uwCwUf5+O53es187aJJFogAFFooAEg4HCcv4Wbn//ACnwA"))
}
E.showMenu({
"": {title:/*LANG*/"Loading Screen"},
"Default" : () => {require("Storage").erase(".loading");goBack()},
"No Screen" : () => {require("Storage").write(".loading","NO");goBack()}, // less than 3 chars and nothing is rendered
"Hourglass" : () => {require("Storage").write(".loading",iconHourglass());goBack()},
"Retro" : () => {require("Storage").write(".loading",iconRetro());goBack()},
"Stripes" : () => savePattern(`
XX..XX..
.XX..XX.
..XX..XX
X..XX..X
XX..XX..
.XX..XX.
..XX..XX
X..XX..X
`),
"Lines" : () => savePattern(`
XXXXXXXX
........
XXXXXXXX
........
XXXXXXXX
........
XXXXXXXX
........
`),
"Dots" : () => savePattern(`
......
..XX..
.XXXX.
.XXXX.
..XX..
......
`)
});
/* For testing, this generates an image with a different colour surrounding on it
require("FontSinclair").add(Graphics);
var b = Graphics.createArrayBuffer(84,12,2);
b.setBgColor(1).clear();
b.transparent = 1;
b.setFont("Sinclair").setColor(0);
for (var y=-2;y<=2;y++) for (var x=-2;x<=2;x++) b.drawString("LOADING...",2+x,2+y);
b.setColor(3).drawString("LOADING...",2,2);
g.drawImage(b.asImage("string"));
*/
})

View File

@ -239,6 +239,8 @@ TODO:
document.getElementById("mapContainer").style.display = "";
document.getElementById("maptiles").style.display="none";
document.getElementById("uploadbuttons").style.display="none";
map.invalidateSize();
map.locate({setView: true, maxZoom: 16, enableHighAccuracy:true});
}
// -----------------------------------------------------

View File

@ -1,2 +1,3 @@
0.01: New App!
0.02: Do first update request 5s after boot to boot up faster
0.03: Fix updating weather too often

View File

@ -1,19 +1,21 @@
{
let waiting = false;
let settings = require("Storage").readJSON("owmweather.json", 1) || {
enabled: false
};
let settings = Object.assign(
require('Storage').readJSON("owmweather.default.json", true) || {},
require('Storage').readJSON("owmweather.json", true) || {}
);
let completion = function(){
waiting = false;
settings.updated = Date.now();
require('Storage').writeJSON("owmweather.json", settings);
}
if (settings.enabled) {
let weather = require("Storage").readJSON('weather.json') || {};
let lastUpdate;
if (weather && weather.weather && weather.weather.time) lastUpdate = weather.weather.time;
if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){
if (!settings.updated || settings.updated + settings.refresh * 1000 * 60 < Date.now()){
setTimeout(() => {
if (!waiting){
waiting = true;

View File

@ -25,7 +25,7 @@ function parseWeather(response) {
json.weather = weather;
require("Storage").writeJSON('weather.json', json);
require("weather").emit("update", json.weather);
if (require("Storage").read("weather")!==undefined) require("weather").emit("update", json.weather);
return undefined;
} else {
return /*LANG*/"Not OWM data";

View File

@ -1,7 +1,7 @@
{ "id": "owmweather",
"name": "OpenWeatherMap weather provider",
"shortName":"OWM Weather",
"version":"0.02",
"version":"0.03",
"description": "Pulls weather from OpenWeatherMap (OWM) API",
"icon": "app.png",
"type": "bootloader",

View File

@ -1 +1,3 @@
0.01: First release
0.02: clock_info now uses app name to maintain settings specifically for this clock face
ensure clockinfo text is usppercase (font doesn't render lowercase)

View File

@ -86,14 +86,16 @@ let clockInfoDraw = (itm, info, options) => {
}
}
g.setFontLECO1976Regular22().setFontAlign(0, 0);
g.drawString(info.text, midx,options.y+options.h-12); // draw the text
g.drawString(info.text.toString().toUpperCase(), midx,options.y+options.h-12); // draw the text
};
let clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
x : 0, y: 0, w: w/2, h:h/2,
draw : clockInfoDraw
});
let clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
x : w/2, y: 0, w: w/2, h:h/2,
draw : clockInfoDraw
});

View File

@ -2,7 +2,7 @@
"id": "pebblepp",
"name": "Pebble++ Clock",
"shortName": "Pebble++",
"version": "0.01",
"version": "0.02",
"description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

View File

@ -1,3 +1,4 @@
0.01: first release
0.02: removed fast load, minimalism is useful for narrowing down on issues
0.03: Use clock_info module as an app
0.04: clock_info now uses app name to maintain settings specifically for this clock face

View File

@ -5,7 +5,7 @@ function draw() {
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);
@ -34,7 +34,7 @@ function queueDraw() {
/**
* 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
*
@ -42,7 +42,7 @@ function queueDraw() {
function 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
@ -54,7 +54,7 @@ function clockInfoDraw(itm, info, options) {
// 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
@ -82,7 +82,7 @@ let clockInfoItems = require("clock_info").load();
* 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"} );
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"simplestpp", x:64, y:132, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#0ff"} );
// Clear the screen once, at startup
g.clear();

View File

@ -2,7 +2,7 @@
"id": "simplestpp",
"name": "Simplest++ Clock",
"shortName": "Simplest++",
"version": "0.03",
"version": "0.04",
"description": "The simplest working clock, with clock_info, acts as a tutorial piece",
"readme": "README.md",
"icon": "app.png",

View File

@ -10,3 +10,4 @@
0.08: Stability improvements - ensure we continue even if a flat string can't be allocated
Stop ClockInfo text drawing outside the allocated area
0.09: Use clock_info module as an app
0.10: Option to hide widgets, tweak top widget width to avoid overlap with hour text at 9am

View File

@ -58,7 +58,8 @@ let draw = function() {
// Now draw this one
R = Bangle.appRect;
x = R.w / 2;
y = R.y + R.h / 2 - 12; // 12 = room for date
y = R.y + R.h / 2 - 6;
if (!settings.hideWidgets) y-= 6; // extra room for date
var date = new Date();
var local_time = require("locale").time(date, 1);
var hourStr = local_time.split(":")[0].trim().padStart(2,'0');
@ -77,6 +78,8 @@ let draw = function() {
g2.setColor(0).fillRect(0,0,g2.getWidth(),g2.getHeight()).setFontAlign(1, 0).setFont("PaytoneOne");
g2.setColor(1).drawString(minStr, g2.getWidth()-fontBorder, g2.getHeight()/2).setFont("4x6"); // draw and unload custom font
g2.setColor(0).fillPoly([0,0, g2.getWidth(),0, 0,slope*2]);
// redraw the top widget
clockInfoMenu.redraw();
// start the animation *in*
animate(true);
};
@ -141,8 +144,14 @@ let clockInfoDraw = (itm, info, options) => {
g.setClipRect(0,0,g.getWidth()-1, g.getHeight()-1);
};
let clockInfoItems = require("clock_info").load();
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { app:"slopeclockpp",x:126, y:24, w:50, h:40, draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#f00"/*red*/ });
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { app:"slopeclockpp",x:0, y:115, w:50, h:40, draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (bgColor=="#000")?"#f00"/*red*/:g.theme.fg });
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, { // top right
app:"slopeclockpp",x:132, y:settings.hideWidgets ? 12 : 24, w:44, h:40,
draw : clockInfoDraw, bg : g.theme.bg, fg : g.theme.fg, hl : "#f00"/*red*/
});
let clockInfoMenu2 = require("clock_info").addInteractive(clockInfoItems, { // bottom left
app:"slopeclockpp",x:0, y:115, w:50, h:40,
draw : clockInfoDraw, bg : bgColor, fg : g.theme.bg, hl : (bgColor=="#000")?"#f00"/*red*/:g.theme.fg
});
// Show launcher when middle button pressed
Bangle.setUI({
@ -163,6 +172,7 @@ Bangle.setUI({
});
// Load widgets
Bangle.loadWidgets();
if (settings.hideWidgets) require("widget_utils").swipeOn();
else setTimeout(Bangle.drawWidgets,0);
draw();
setTimeout(Bangle.drawWidgets,0);
}
}

View File

@ -1,6 +1,6 @@
{ "id": "slopeclockpp",
"name": "Slope Clock ++",
"version":"0.09",
"version":"0.10",
"description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows extra information and allows the colors to be selected.",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -15,6 +15,10 @@
let menu ={
'': { 'title': 'Slope Clock ++' },
/*LANG*/'< Back': back,
/*LANG*/'Hide Widgets': {
value: !!settings.hideWidgets,
onchange: x => save('hideWidgets', x),
},
/*LANG*/'Red': {
value: !!settings.colorRed,
format: () => (settings.colorRed ? 'Yes' : 'No'),

View File

@ -1,2 +1,3 @@
0.01: New app!
0.02: Submitted to the app loader
0.02: Submitted to the app loader
0.03: Fix going back from a lap view, and add a main-menu back button

View File

@ -53,7 +53,7 @@ function view(fileName) {
let lapMenu = {
'': {
'title': fileNameToDateString(fileName),
'back': () => { E.showMenu(mainMenu); }
'back': () => showMainMenu()
},
};
lapMenu[`Total time: ${msToHumanReadable(fileData[fileData.length - 1])}`] = () => { };
@ -89,15 +89,16 @@ function showMainMenu() {
let mainMenu = {
'': {
'title': 'Sessions'
'title': 'Sessions',
'back': () => load()
}
};
//I know eval is evil, but I can't think of any other way to do this.
for (let lapFile of LAP_FILES) {
mainMenu[fileNameToDateString(lapFile)] = eval(`(function() {
view('${lapFile}');
})`);
// `let` variables in JS have special behaviour in loops,
// where capturing them captures that instance of the variable,
// but for espruino we need to do a slightly older trick:
mainMenu[fileNameToDateString(lapFile)] = ((lapFile) => () => view(lapFile))(lapFile);
}
if (LAP_FILES.length == 0) {

View File

@ -1,7 +1,7 @@
{
"id": "stlapview",
"name": "Stopwatch laps",
"version": "0.02",
"version": "0.03",
"description": "Optional lap viewer for my stopwatch app",
"icon": "icon.png",
"type": "app",

View File

@ -1 +1,2 @@
0.01: New Widget!
0.02: Now use an app ID (to avoid conflicts with clocks that also use ClockInfo)

View File

@ -1,8 +1,8 @@
{ "id": "widclkinfo",
"name": "Clock Info Widget",
"version":"0.01",
"version":"0.02",
"description": "Use 'Clock Info' in the Widget bar. Tap on the widget to select, then drag up/down/left/right to choose what information is displayed.",
"icon": "widget.png",
"icon": "widget.png",
"screenshots" : [ { "url":"screenshot.png" }],
"type": "widget",
"tags": "widget,clkinfo",

View File

@ -3,6 +3,7 @@ if (!require("clock_info").loadCount) { // don't load if a clock_info was alread
let clockInfoItems = require("clock_info").load();
// Add the
let clockInfoMenu = require("clock_info").addInteractive(clockInfoItems, {
app : "widclkinfo",
// Add the dimensions we're rendering to here - these are used to detect taps on the clock info area
x : 0, y: 0, w: 72, h:24,
// You can add other information here you want to be passed into 'options' in 'draw'
@ -19,8 +20,8 @@ if (!require("clock_info").loadCount) { // don't load if a clock_info was alread
let clockInfoInfo; // when clockInfoMenu.draw is called we set this up
// The actual widget we're displaying
WIDGETS["clkinfo"] = {
area:"tl",
WIDGETS["clkinfo"] = {
area:"tl",
width: clockInfoMenu.w,
draw:function(e) {
clockInfoMenu.x = e.x;

View File

@ -1,3 +1,5 @@
0.01: New widget - music control via a swipe
0.02: Improve interactivity - avoid responding to swipes when a menu or
launcher is active.
0.03: Handle errors when sending input over BLE and the special-case of
replacing a single handler

View File

@ -2,7 +2,7 @@
"id": "widhid",
"name": "Bluetooth Music Swipe Control Widget",
"shortName": "BLE Swipe Widget",
"version": "0.02",
"version": "0.03",
"description": "Based on Swipe Bluetooth Music Controls (based on Bluetooth Music Controls). Swipe down to enable, then swipe up/down for volume, left/right for previous and next and tap for play/pause. Enable HID in settings, pair with your phone/computer, then use this widget to control music from your watch!",
"icon": "icon.png",
"readme": "README.md",

View File

@ -81,7 +81,10 @@
if (!wasActive) {
waitForRelease = true;
Bangle.on("drag", onDrag);
Bangle["#ondrag"] = [onDrag].concat(Bangle["#ondrag"].filter(function (f) { return f !== onDrag; }));
var dragHandlers = Bangle["#ondrag"];
if (dragHandlers && typeof dragHandlers !== "function") {
Bangle["#ondrag"] = [onDrag].concat(dragHandlers.filter(function (f) { return f !== onDrag; }));
}
redraw();
}
if (activeTimeout)
@ -120,7 +123,12 @@
redraw();
});
var sendHid = function (code) {
NRF.sendHIDReport([1, code], function () { return NRF.sendHIDReport([1, 0]); });
try {
NRF.sendHIDReport([1, code], function () { return NRF.sendHIDReport([1, 0]); });
}
catch (e) {
console.log("sendHIDReport:", e);
}
};
var next = function () { return sendHid(0x01); };
var prev = function () { return sendHid(0x02); };

View File

@ -23,7 +23,7 @@
const onDrag = (e => {
// Espruino/35c8cb9be11
(E as any).stopEventPropagation && (E as any).stopEventPropagation();
E.stopEventPropagation && E.stopEventPropagation();
if(e.b === 0){
// released
@ -84,10 +84,15 @@
waitForRelease = true; // wait for first touch up before accepting gestures
Bangle.on("drag", onDrag);
// move our drag to the start of the event listener array
(Bangle as any)["#ondrag"] = [onDrag].concat(
(Bangle as any)["#ondrag"].filter((f: unknown) => f !== onDrag)
);
const dragHandlers = (Bangle as BangleEvents)["#ondrag"]
if(dragHandlers && typeof dragHandlers !== "function"){
(Bangle as BangleEvents)["#ondrag"] = [onDrag as undefined | typeof onDrag].concat(
dragHandlers.filter((f: unknown) => f !== onDrag)
);
}
redraw();
}
@ -140,10 +145,14 @@
//const DEBUG = true;
const sendHid = (code: number) => {
//if(DEBUG) return;
NRF.sendHIDReport(
[1, code],
() => NRF.sendHIDReport([1, 0]),
);
try{
NRF.sendHIDReport(
[1, code],
() => NRF.sendHIDReport([1, 0]),
);
}catch(e){
console.log("sendHIDReport:", e);
}
};
const next = () => /*DEBUG ? console.log("next") : */ sendHid(0x01);

View File

@ -195,7 +195,7 @@
</footer>
<script src="webtools/puck.js"></script>
<script src="webtools/heatshrink.js"></script>
<script src="webtools/heatshrink.js"></script>
<script src="core/lib/marked.min.js"></script>
<script src="core/lib/espruinotools.js"></script>
<script src="core/js/utils.js"></script>

10
typescript/types/bangle_extensions.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
type BangleHandler<T extends (...args: any[]) => any> = T | (T | undefined)[];
type BangleEvents = {
["#ontap"]?: BangleHandler<(data: { dir: "left" | "right" | "top" | "bottom" | "front" | "back", double: boolean, x: TapAxis, y: TapAxis, z: TapAxis }) => void>,
["#ongesture"]?: BangleHandler<(xyz: Int8Array) => void>,
["#onswipe"]?: BangleHandler<SwipeCallback>,
["#ontouch"]?: BangleHandler<TouchCallback>,
["#ondrag"]?: BangleHandler<DragCallback>,
["#onstroke"]?: BangleHandler<(event: { xy: Uint8Array, stroke?: string }) => void>,
};

View File

@ -465,6 +465,46 @@ declare class ESP32 {
*/
static deepSleep(us: number): void;
/**
* Put device in deepsleep state until interrupted by pin "pin".
* Eligible pin numbers are restricted to those [GPIOs designated
* as RTC GPIOs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary).
*
* @param {Pin} pin - Pin to trigger wakeup
* @param {number} level - Logic level to trigger
* @url http://www.espruino.com/Reference#l_ESP32_deepSleepExt0
*/
static deepSleepExt0(pin: Pin, level: number): void;
/**
* Put device in deepsleep state until interrupted by pins in the "pinVar" array.
* The trigger "mode" determines the pin state which will wake up the device.
* Valid modes are:
* * `0: ESP_EXT1_WAKEUP_ALL_LOW` - all nominated pins must be set LOW to trigger wakeup
* * `1: ESP_EXT1_WAKEUP_ANY_HIGH` - any of nominated pins set HIGH will trigger wakeup
* Eligible pin numbers are restricted to those [GPIOs designated
* as RTC GPIOs](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary).
*
* @param {any} pinVar - Array of Pins to trigger wakeup
* @param {number} mode - Trigger mode
* @url http://www.espruino.com/Reference#l_ESP32_deepSleepExt1
*/
static deepSleepExt1(pinVar: any, mode: number): void;
/**
* Returns a variable identifying the cause of wakeup from deep sleep.
* Possible causes include:
* * `0: ESP_SLEEP_WAKEUP_UNDEFINED` - reset was not caused by exit from deep sleep
* * `2: ESP_SLEEP_WAKEUP_EXT0` - Wakeup caused by external signal using RTC_IO
* * `3: ESP_SLEEP_WAKEUP_EXT1` - Wakeup caused by external signal using RTC_CNTL
* * `4: ESP_SLEEP_WAKEUP_TIMER` - Wakeup caused by timer
* * `5: ESP_SLEEP_WAKEUP_TOUCHPAD` - Wakeup caused by touchpad
* * `6: ESP_SLEEP_WAKEUP_ULP` - Wakeup caused by ULP program
* @returns {number} The cause of the ESP32's wakeup from sleep
* @url http://www.espruino.com/Reference#l_ESP32_getWakeupCause
*/
static getWakeupCause(): number;
/**
* Returns an object that contains details about the state of the ESP32 with the
* following fields:
@ -5597,7 +5637,9 @@ declare class Graphics<IsBuffer extends boolean = boolean> {
* `width,height,bpp,[transparent,]image_bytes...`. If a transparent colour is
* specified the top bit of `bpp` should be set.
* * An ArrayBuffer Graphics object (if `bpp<8`, `msb:true` must be set) - this is
* disabled on devices without much flash memory available
* disabled on devices without much flash memory available. If a Graphics object
* is supplied, it can also contain transparent/palette fields as if it were
* an image.
* Draw an image at the specified position.
* * If the image is 1 bit, the graphics foreground/background colours will be
* used.
@ -5678,6 +5720,9 @@ declare class Graphics<IsBuffer extends boolean = boolean> {
* * Is 8 bpp *OR* the `{msb:true}` option was given
* * No other format options (zigzag/etc) were given
* Otherwise data will be copied, which takes up more space and may be quite slow.
* If the `Graphics` object contains `transparent` or `pelette` fields,
* [as you might find in an image](http://www.espruino.com/Graphics#images-bitmaps),
* those will be included in the generated image too.
*
* @param {any} type - The type of image to return. Either `object`/undefined to return an image object, or `string` to return an image string
* @returns {any} An Image that can be used with `Graphics.drawImage`
@ -5800,6 +5845,19 @@ declare class Graphics<IsBuffer extends boolean = boolean> {
*/
transformVertices(arr: number[], transformation: { x?: number, y?: number, scale?: number, rotate?: number } | [number, number, number, number, number, number]): number[];
/**
* Flood fills the given Graphics instance out from a particular point.
* **Note:** This only works on Graphics instances that support readback with `getPixel`. It
* is also not capable of filling over dithered patterns (eg non-solid colours on Bangle.js 2)
*
* @param {number} x - X coordinate to start from
* @param {number} y - Y coordinate to start from
* @param {any} col - The color to fill with (if undefined, foreground is used)
* @returns {any} The instance of Graphics this was called on, to allow call chaining
* @url http://www.espruino.com/Reference#l_Graphics_floodFill
*/
floodFill(x: number, y: number, col: any): Graphics;
/**
* Returns an object of the form:
* ```
@ -8223,6 +8281,10 @@ declare class E {
/**
* Dump any locked variables that aren't referenced from `global` - for debugging
* memory leaks only.
* **Note:** This does a linear scan over memory, finding variables
* that are currently locked. In some cases it may show variables
* like `Unknown 66` which happen when *part* of a string has ended
* up placed in memory ahead of the String that it's part of. See https://github.com/espruino/Espruino/issues/2345
* @url http://www.espruino.com/Reference#l_E_dumpLockedVars
*/
static dumpLockedVars(): void;
@ -8655,6 +8717,24 @@ declare class E {
*/
static decodeUTF8(str: string, lookup: string[], replaceFn: string | ((charCode: number) => string)): string;
/**
* When using events with `X.on('foo', function() { ... })`
* and then `X.emit('foo')` you might want to stop subsequent
* event handlers from being executed.
* Calling this function doing the execution of events will
* ensure that no subsequent event handlers are executed.
* ```
* var X = {}; // in Espruino all objects are EventEmitters
* X.on('foo', function() { print("A"); })
* X.on('foo', function() { print("B"); E.stopEventPropagation(); })
* X.on('foo', function() { print("C"); })
* X.emit('foo');
* // prints A,B but not C
* ```
* @url http://www.espruino.com/Reference#l_E_stopEventPropagation
*/
static stopEventPropagation(): void;
}
@ -8959,6 +9039,9 @@ interface Object {
* o.emit('answer', 44);
* // nothing printed
* ```
* If you have more than one handler for an event, and you'd
* like that handler to stop the event being passed to other handlers
* then you can call `E.stopEventPropagation()` in that handler.
*
* @param {any} event - The name of the event, for instance 'data'
* @param {any} listener - The listener to call when this event is received
@ -10074,6 +10157,12 @@ declare class Serial {
* @url http://www.espruino.com/Reference#l_Serial_pipe
*/
pipe(destination: any, options?: PipeOptions): void
/**
* Flush this serial stream (pause execution until all data has been sent)
* @url http://www.espruino.com/Reference#l_Serial_flush
*/
flush(): void;
}
interface StringConstructor {