aiclock -- support clkinfo module and updated icons of some clkinfos...

pull/2349/head
David Peer 2022-12-04 16:42:24 +01:00
parent 11e5dc5905
commit fa70e5354c
18 changed files with 270 additions and 50 deletions

View File

@ -2,3 +2,4 @@
0.02: Design improvements and fixes. 0.02: Design improvements and fixes.
0.03: Indicate battery level through line occurrence. 0.03: Indicate battery level through line occurrence.
0.04: Use widget_utils module. 0.04: Use widget_utils module.
0.05: Support for clkinfo.

View File

@ -10,7 +10,9 @@ The original output of stable diffusion is shown here:
My implementation is shown below. Note that horizontal lines occur randomly, but the My implementation is shown below. Note that horizontal lines occur randomly, but the
probability is correlated with the battery level. So if your screen contains only probability is correlated with the battery level. So if your screen contains only
a few lines its time to charge your bangle again ;) a few lines its time to charge your bangle again ;) Also note that the upper text
implementes the clkinfo module and can be configured via touch left/right/up/down.
Touch at the center to trigger the selected action.
![](impl.png) ![](impl.png)

View File

@ -1,6 +1,14 @@
/** /************************************************
* AI Clock * AI Clock
*/ */
const storage = require('Storage');
const clock_info = require("clock_info");
/************************************************
* Assets
*/
require("Font7x11Numeric7Seg").add(Graphics); require("Font7x11Numeric7Seg").add(Graphics);
Graphics.prototype.setFontGochiHand = function(scale) { Graphics.prototype.setFontGochiHand = function(scale) {
// Actual height 27 (29 - 3) // Actual height 27 (29 - 3)
@ -13,7 +21,7 @@ Graphics.prototype.setFontGochiHand = function(scale) {
return this; return this;
} }
/* /************************************************
* Set some important constants such as width, height and center * Set some important constants such as width, height and center
*/ */
var W = g.getWidth(),R=W/2; var W = g.getWidth(),R=W/2;
@ -21,6 +29,120 @@ var H = g.getHeight();
var cx = W/2; var cx = W/2;
var cy = H/2; var cy = H/2;
var drawTimeout; var drawTimeout;
var lock_input = false;
/************************************************
* SETTINGS
*/
const SETTINGS_FILE = "aiclock.setting.json";
let settings = {
menuPosX: 0,
menuPosY: 0,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
settings[key] = saved_settings[key]
}
/************************************************
* Menu
*/
function getDate(){
var date = new Date();
return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2)
}
// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file.
var clockItems = {
name: getDate(),
img: null,
items: [
{ name: "Week",
get: () => ({ text: "Week " + weekOfYear(), img: null}),
show: function() { clockItems.items[0].emit("redraw"); },
hide: function () {}
},
]
};
function weekOfYear() {
var date = new Date();
date.setHours(0, 0, 0, 0);
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
// January 4 is always in week 1.
var week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
- 3 + (week1.getDay() + 6) % 7) / 7);
}
// Load menu
var menu = clock_info.load();
menu = menu.concat(clockItems);
// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it.
if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){
settings.menuPosX = 0;
settings.menuPosY = 0;
}
// Set draw functions for each item
menu.forEach((menuItm, x) => {
menuItm.items.forEach((item, y) => {
function drawItem() {
// For the clock, we have a special case, as we don't wanna redraw
// immediately when something changes. Instead, we update data each minute
// to save some battery etc. Therefore, we hide (and disable the listener)
// immedeately after redraw...
item.hide();
// After drawing the item, we enable inputs again...
lock_input = false;
var info = item.get();
drawMenuItem(info.text, info.img);
}
item.on('redraw', drawItem);
})
});
function canRunMenuItem(){
if(settings.menuPosY == 0){
return false;
}
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
return item.run !== undefined;
}
function runMenuItem(){
if(settings.menuPosY == 0){
return;
}
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
try{
var ret = item.run();
if(ret){
Bangle.buzz(300, 0.6);
}
} catch (ex) {
// Simply ignore it...
}
}
/* /*
* Based on the great multi clock from https://github.com/jeffmer/BangleApps/ * Based on the great multi clock from https://github.com/jeffmer/BangleApps/
@ -76,7 +198,50 @@ function toAngle(a){
return a return a
} }
function drawMenuItem(text, image){
if(text == null){
drawTime();
return
}
// image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==");
text = String(text);
g.reset().setBgColor("#fff").setColor("#000");
g.setFontAlign(0,0);
g.setFont("Vector", 20);
var imgWidth = image == null ? 0 : 24;
var strWidth = g.stringWidth(text);
var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2);
var w = imgWidth + strWidth;
g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2)
// Draw right line as designed by stable diffusion
g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2);
g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2);
g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2);
// And finally the text
g.drawString(text, cx+imgWidth/2, 42);
g.drawString(text, cx+1+imgWidth/2, 41);
if(image != null) {
var scale = image.width ? imgWidth / image.width : 1;
g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale});
}
drawTime();
}
function drawTime(){ function drawTime(){
// Draw digital time first
drawDigits();
// And now the analog time
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38); var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 ); var drawMinuteHand = g.drawRotRect.bind(g,6,12,R-12 );
@ -90,13 +255,6 @@ function drawTime(){
h += date.getMinutes()/60.0; h += date.getMinutes()/60.0;
h = parseInt(h*360/12); h = parseInt(h*360/12);
// Draw minute and hour bg
g.setColor(g.theme.bg);
drawHourHand(toAngle(h-3));
drawHourHand(toAngle(h+3));
drawMinuteHand(toAngle(m-2));
drawMinuteHand(toAngle(m+3));
// Draw minute and hour fg // Draw minute and hour fg
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
drawHourHand(h); drawHourHand(h);
@ -104,28 +262,6 @@ function drawTime(){
} }
function drawDate(){
var date = new Date();
g.setFontAlign(0,0);
g.setFontGochiHand();
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2);
var w = g.stringWidth(text);
g.setColor(g.theme.bg);
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
g.setColor(g.theme.fg);
// Draw right line as designed by stable diffusion
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
// And finally the text
g.drawString(text, cx, 40);
}
function drawDigits(){ function drawDigits(){
var date = new Date(); var date = new Date();
@ -156,20 +292,35 @@ function drawDigits(){
} }
function drawDate(){
var menuEntry = menu[settings.menuPosX];
// The first entry is the overview...
if(settings.menuPosY == 0){
drawMenuItem(menuEntry.name, menuEntry.img);
return;
}
// Draw item if needed
lock_input = true;
var item = menuEntry.items[settings.menuPosY-1];
item.show();
}
function draw(){ function draw(){
// Queue draw in one minute // Queue draw in one minute
queueDraw(); queueDraw();
g.reset(); g.reset();
g.clearRect(0, 0, g.getWidth(), g.getHeight()); g.clearRect(0, 0, g.getWidth(), g.getHeight());
g.setColor(1,1,1); g.setColor(1,1,1);
drawBackground(); drawBackground();
drawDate(); drawDate();
drawDigits();
drawTime();
drawCircle(Bangle.isLocked()); drawCircle(Bangle.isLocked());
} }
@ -190,6 +341,68 @@ Bangle.on('lock', function(isLocked) {
drawCircle(isLocked); drawCircle(isLocked);
}); });
Bangle.on('touch', function(btn, e){
var left = parseInt(g.getWidth() * 0.22);
var right = g.getWidth() - left;
var upper = parseInt(g.getHeight() * 0.22);
var lower = g.getHeight() - upper;
var is_upper = e.y < upper;
var is_lower = e.y > lower;
var is_left = e.x < left && !is_upper && !is_lower;
var is_right = e.x > right && !is_upper && !is_lower;
var is_center = !is_upper && !is_lower && !is_left && !is_right;
if(lock_input){
return;
}
if(is_lower){
Bangle.buzz(40, 0.6);
settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
draw();
}
if(is_upper){
Bangle.buzz(40, 0.6);
settings.menuPosY = settings.menuPosY-1;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
draw();
}
if(is_right){
Bangle.buzz(40, 0.6);
settings.menuPosX = (settings.menuPosX+1) % menu.length;
settings.menuPosY = 0;
draw();
}
if(is_left){
Bangle.buzz(40, 0.6);
settings.menuPosY = 0;
settings.menuPosX = settings.menuPosX-1;
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
draw();
}
if(is_center){
if(canRunMenuItem()){
runMenuItem();
}
}
});
E.on("kill", function(){
try{
storage.write(SETTINGS_FILE, settings);
} catch(ex){
// If this fails, we still kill the app...
}
});
/* /*
* Some helpers * Some helpers
@ -203,7 +416,6 @@ function queueDraw() {
} }
/* /*
* Lets start widgets, listen for btn etc. * Lets start widgets, listen for btn etc.
*/ */
@ -216,6 +428,7 @@ Bangle.loadWidgets();
* area to the top bar doesn't get cleared. * area to the top bar doesn't get cleared.
*/ */
require('widget_utils').hide(); require('widget_utils').hide();
// Clear the screen once, at startup and draw clock // Clear the screen once, at startup and draw clock
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
draw(); draw();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
apps/aiclock/impl_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
apps/aiclock/impl_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -3,7 +3,7 @@
"name": "AI Clock", "name": "AI Clock",
"shortName":"AI Clock", "shortName":"AI Clock",
"icon": "aiclock.png", "icon": "aiclock.png",
"version":"0.04", "version":"0.05",
"readme": "README.md", "readme": "README.md",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
@ -11,7 +11,9 @@
"tags": "clock", "tags": "clock",
"screenshots": [ "screenshots": [
{"url":"orig.png"}, {"url":"orig.png"},
{"url":"impl.png"} {"url":"impl.png"},
{"url":"impl_2.png"},
{"url":"impl_3.png"},
], ],
"storage": [ "storage": [
{"name":"aiclock.app.js","url":"aiclock.app.js"}, {"name":"aiclock.app.js","url":"aiclock.app.js"},

View File

@ -2,4 +2,5 @@
0.02: Includeas the ha.lib.js library that can be used by other apps or clocks. 0.02: Includeas the ha.lib.js library that can be used by other apps or clocks.
0.03: Added clkinfo for clocks. 0.03: Added clkinfo for clocks.
0.04: Feedback if clkinfo run is called. 0.04: Feedback if clkinfo run is called.
0.05: Clkinfo improvements. 0.05: Clkinfo improvements.
0.06: Updated clkinfo icon.

View File

@ -4,7 +4,7 @@
var haItems = { var haItems = {
name: "Home", name: "Home",
img: atob("GBiBAf/////////n///D//+B//8A//48T/wkD/gkD/A8D+AYB8AYA4eZ4QyZMOyZN+fb5+D/B+B+B+A8B+AYB+AYB+AYB+AYB+A8Bw=="), img: atob("GBiBAAAAAAAAAAAAAAAYAAA+AAB+AADD4AHb4APD4Afn8A/n+BxmOD0mnA0ksAwAMA+B8A/D8A/n8A/n8A/n8A/n8AAAAAAAAAAAAA=="),
items: [] items: []
}; };

View File

@ -1,7 +1,7 @@
{ {
"id": "ha", "id": "ha",
"name": "HomeAssistant", "name": "HomeAssistant",
"version": "0.05", "version": "0.06",
"description": "Integrates your BangleJS into HomeAssistant.", "description": "Integrates your BangleJS into HomeAssistant.",
"icon": "ha.png", "icon": "ha.png",
"type": "app", "type": "app",

View File

@ -1,4 +1,5 @@
0.01: Release 0.01: Release
0.02: Rewrite with new interface 0.02: Rewrite with new interface
0.03: Added clock infos to expose timer functionality to clocks. 0.03: Added clock infos to expose timer functionality to clocks.
0.04: Improvements of clock infos. 0.04: Improvements of clock infos.
0.05: Updated clkinfo icon.

View File

@ -63,10 +63,9 @@
} catch(ex){ } } catch(ex){ }
} }
var img = atob("GBiBAeAAB+AAB/v/3/v/3/v/3/v/3/v/n/n/H/z+P/48//85//+b//+b//8p//4E//yCP/kBH/oAn/oAX/oAX/oAX/oAX+AAB+AABw==")
var smpltmrItems = { var smpltmrItems = {
name: "Timer", name: "Timer",
img: img, img: atob("GBiBAAB+AAB+AAAYAAAYAAB+AA3/sA+B8A4AcAwMMBgPGBgPmDAPjDAPzDAPzDP/zDP/zDH/jBn/mBj/GAw8MA4AcAeB4AH/gAB+AA=="),
items: [ items: [
{ {
name: null, name: null,

View File

@ -2,7 +2,7 @@
"id": "smpltmr", "id": "smpltmr",
"name": "Simple Timer", "name": "Simple Timer",
"shortName": "Simple Timer", "shortName": "Simple Timer",
"version": "0.04", "version": "0.05",
"description": "A very simple app to start a timer.", "description": "A very simple app to start a timer.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,timer,clkinfo", "tags": "tool,alarm,timer,clkinfo",

View File

@ -17,3 +17,4 @@
0.18: Added hasRange to clkinfo. 0.18: Added hasRange to clkinfo.
0.19: Added weather condition to clkinfo. 0.19: Added weather condition to clkinfo.
0.20: Added weather condition with temperature to clkinfo. 0.20: Added weather condition with temperature to clkinfo.
0.21: Updated clkinfo icon.

View File

@ -28,7 +28,7 @@
//FIXME ranges are somehow arbitrary //FIXME ranges are somehow arbitrary
var weatherItems = { var weatherItems = {
name: "Weather", name: "Weather",
img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="), img: atob("GBiBAABAAARGAAYEAADgACP4wDf8gAf+AA/+AE/4AG/jwA/v4A8P8AR/8DH/8AH//AP//g///g///g///gf//AAAAABkwABMgABMgA=="),
items: [ items: [
{ {
name: "conditionWithTemperature", name: "conditionWithTemperature",

View File

@ -1,7 +1,7 @@
{ {
"id": "weather", "id": "weather",
"name": "Weather", "name": "Weather",
"version": "0.20", "version": "0.21",
"description": "Show Gadgetbridge weather report", "description": "Show Gadgetbridge weather report",
"icon": "icon.png", "icon": "icon.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],

2
core

@ -1 +1 @@
Subproject commit 3a953179b7bb9f574d4e77d5f34b6b7deee1e884 Subproject commit 76419750083a88ee7a569db3975ae1bdd6dc155a

View File

@ -77,7 +77,7 @@ exports.load = function() {
// actual menu // actual menu
var menu = [{ var menu = [{
name: "Bangle", name: "Bangle",
img: atob("GBiBAf8B//4B//4B//4B//4A//x4//n+f/P/P+fPn+fPn+fP3+/Px+/Px+fn3+fzn+f/n/P/P/n+f/x4//4A//4B//4B//4B//8B/w=="), img: atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA=="),
items: [ items: [
{ name : "Battery", { name : "Battery",
hasRange : true, hasRange : true,