Merge pull request #2133 from peerdavid/master

[BWClock] Use the clkinfo module to dynamically add new features to the clock as apps are installed by the user
master
Gordon Williams 2022-09-23 08:54:06 +01:00 committed by GitHub
commit 4feb1cf002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 336 additions and 562 deletions

View File

@ -3,3 +3,4 @@
0.03: Disable past events display from settings 0.03: Disable past events display from settings
0.04: Added awareness of allDay field 0.04: Added awareness of allDay field
0.05: Displaying calendar colour and name 0.05: Displaying calendar colour and name
0.06: Added clkinfo for clocks.

View File

@ -0,0 +1,29 @@
(function() {
var agendaItems = {
name: "Agenda",
img: atob("GBiBAf////////85z/AAAPAAAPgAAP////AAAPAAAPAAAPAAAOAAAeAAAeAAAcAAA8AAAoAABgAADP//+P//8PAAAPAAAPgAAf///w=="),
items: []
};
var now = new Date();
var agenda = storage.readJSON("android.calendar.json")
.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000)
.sort((a,b)=>a.timestamp - b.timestamp);
agenda.forEach((entry, i) => {
var title = entry.title.slice(0,18);
var date = new Date(entry.timestamp*1000);
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
agendaItems.items.push({
name: "agendaEntry-" + i,
get: () => ({ text: title + "\n" + dateStr, img: null}),
show: function() { agendaItems.items[i].emit("redraw"); },
hide: function () {}
});
});
return agendaItems;
})

View File

@ -1,7 +1,7 @@
{ {
"id": "agenda", "id": "agenda",
"name": "Agenda", "name": "Agenda",
"version": "0.05", "version": "0.06",
"description": "Simple agenda", "description": "Simple agenda",
"icon": "agenda.png", "icon": "agenda.png",
"screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}], "screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}],
@ -12,6 +12,7 @@
"storage": [ "storage": [
{"name":"agenda.app.js","url":"agenda.js"}, {"name":"agenda.app.js","url":"agenda.js"},
{"name":"agenda.settings.js","url":"settings.js"}, {"name":"agenda.settings.js","url":"settings.js"},
{"name":"agenda.clkinfo.js","url":"agenda.clkinfo.js"},
{"name":"agenda.img","url":"agenda-icon.js","evaluate":true} {"name":"agenda.img","url":"agenda-icon.js","evaluate":true}
], ],
"data": [{"name":"agenda.settings.json"}] "data": [{"name":"agenda.settings.json"}]

View File

@ -19,3 +19,4 @@
0.19: Fix - Compatibility with "Digital clock widget" 0.19: Fix - Compatibility with "Digital clock widget"
0.20: Better handling of async data such as getPressure. 0.20: Better handling of async data such as getPressure.
0.21: On the default menu the week of year can be shown. 0.21: On the default menu the week of year can be shown.
0.22: Use the new clkinfo module for the menu.

View File

@ -1,12 +1,15 @@
# BW Clock # BW Clock
A very minimalistic clock to mainly show date and time. A very minimalistic clock.
![](screenshot.png) ![](screenshot.png)
## Features ## Features
The BW clock provides many features and also 3rd party integrations: The BW clock implements features that are exposed by other apps through the `clkinfo` module.
For example, if you install the HomeAssistant app, this menu item will be shown if you click right
and additionally allows you to send triggers directly from the clock (select triggers via up/down and
send via click center). Here are examples of other apps that are integrated:
- Bangle data such as steps, heart rate, battery or charging state. - Bangle data such as steps, heart rate, battery or charging state.
- A timer can be set directly. *Requirement: Scheduler library*
- Show agenda entries. A timer for an agenda entry can also be set by simply clicking in the middle of the screen. This can be used to not forget a meeting etc. Note that only one agenda-timer can be set at a time. *Requirement: Gadgetbridge calendar sync enabled* - Show agenda entries. A timer for an agenda entry can also be set by simply clicking in the middle of the screen. This can be used to not forget a meeting etc. Note that only one agenda-timer can be set at a time. *Requirement: Gadgetbridge calendar sync enabled*
- Weather temperature as well as the wind speed can be shown. *Requirement: Weather app* - Weather temperature as well as the wind speed can be shown. *Requirement: Weather app*
- HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app* - HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app*
@ -20,27 +23,27 @@ Note: If some apps are not installed (e.gt. weather app), then this menu item is
- Your bangle uses the sys color settings so you can change the color too. - Your bangle uses the sys color settings so you can change the color too.
## Menu structure ## Menu structure
2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to set a timer or send a HomeAssistant trigger. 2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to trigger HomeAssistant.
Simply click left / right to go through the menu entries such as Bangle, Timer etc. Simply click left / right to go through the menu entries such as Bangle, Weather etc.
and click up/down to move into this sub-menu. You can then click in the middle of the screen and click up/down to move into this sub-menu. You can then click in the middle of the screen
to e.g. send a trigger via HomeAssistant once you selected it. to e.g. send a trigger via HomeAssistant once you selected it. The actions really depend
on the app that provide this sub-menu through the `clkinfo` module.
``` ```
+5min Bangle -- Agenda -- Weather -- HomeAssistant
| | | | |
Bangle -- Timer[Optional] -- Agenda 1[Optional] -- Weather[Optional] -- HomeAssistant [Optional] Battery Entry 1 Temperature Trigger1
| | | | | | | | |
Bpm -5min Agenda 2 Temperature Trigger1 Steps ... ... ...
| | | |
Steps ... ... ...
| |
Battery ...
``` ```
## Thanks to ## Thanks to
<a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a> - Thanks to Gordon Williams not only for the great BangleJs, but specifically also for the implementation of `clkinfo` which simplified the BWClock a lot and moved complexety to the apps where it should be located.
- <a href="https://www.flaticon.com/free-icons/" title="Icons">Icons created by Flaticon</a>
## Creator ## Creator
[David Peer](https://github.com/peerdavid) [David Peer](https://github.com/peerdavid)

View File

@ -1,19 +1,21 @@
/************ /************************************************
* Includes * Includes
*/ */
const locale = require('locale'); const locale = require('locale');
const storage = require('Storage'); const storage = require('Storage');
const clock_info = require("clock_info");
/************
* Statics /************************************************
* Globals
*/ */
const SETTINGS_FILE = "bwclk.setting.json"; const SETTINGS_FILE = "bwclk.setting.json";
const TIMER_IDX = "bwclk_timer";
const TIMER_AGENDA_IDX = "bwclk_agenda";
const W = g.getWidth(); const W = g.getWidth();
const H = g.getHeight(); const H = g.getHeight();
var lock_input = false;
/************
/************************************************
* Settings * Settings
*/ */
let settings = { let settings = {
@ -29,8 +31,7 @@ for (const key in saved_settings) {
settings[key] = saved_settings[key] settings[key] = saved_settings[key]
} }
/************************************************
/************
* Assets * Assets
*/ */
// Manrope font // Manrope font
@ -45,14 +46,12 @@ Graphics.prototype.setLargeFont = function(scale) {
return this; return this;
}; };
Graphics.prototype.setMediumFont = function(scale) { Graphics.prototype.setMediumFont = function(scale) {
// Actual height 41 (42 - 2) // Actual height 41 (42 - 2)
this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAB/AAAAAAAP/AAAAAAD//AAAAAA///AAAAAP///AAAAB///8AAAAf///AAAAH///wAAAB///+AAAAH///gAAAAH//4AAAAAH/+AAAAAAH/wAAAAAAH8AAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAH////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gAAH+AAD+AAAD/AAH8AAAB/AAH4AAAA/gAH4AAAAfgAH4AAAAfgAPwAAAAfgAPwAAAAfgAPwAAAAfgAHwAAAAfgAH4AAAAfgAH4AAAA/gAH8AAAA/AAD+AAAD/AAD/gAAH/AAB/////+AAB/////8AAA/////4AAAf////wAAAH////gAAAB///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAfwAAAAAAA/gAAAAAAA/AAAAAAAB/AAAAAAAD+AAAAAAAD8AAAAAAAH8AAAAAAAH//////AAH//////AAH//////AAH//////AAH//////AAH//////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAA/AAAP4AAB/AAAf4AAD/AAA/4AAD/AAB/4AAH/AAD/4AAP/AAH/AAAf/AAH8AAA//AAH4AAB//AAP4AAD//AAPwAAH+/AAPwAAP8/AAPwAAf4/AAPwAA/4/AAPwAA/w/AAPwAB/g/AAPwAD/A/AAP4AH+A/AAH8AP8A/AAH/A/4A/AAD///wA/AAD///gA/AAB///AA/AAA//+AA/AAAP/8AA/AAAD/wAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAH4AAAHwAAH4AAAH4AAH4AAAH8AAH4AAAP+AAH4AAAH+AAH4A4AB/AAH4A+AA/AAH4B/AA/gAH4D/AAfgAH4H+AAfgAH4P+AAfgAH4f+AAfgAH4/+AAfgAH5/+AAfgAH5//AAfgAH7+/AA/gAH/8/gB/AAH/4f4H/AAH/wf//+AAH/gP//8AAH/AH//8AAH+AD//wAAH8AB//gAAD4AAf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAD/AAAAAAAP/AAAAAAB//AAAAAAH//AAAAAAf//AAAAAB///AAAAAH///AAAAAf/8/AAAAB//w/AAAAH/+A/AAAA//4A/AAAD//gA/AAAH/+AA/AAAH/4AA/AAAH/gAA/AAAH+AAA/AAAHwAAA/AAAHAAf///AAEAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAP/AHgAAH///AP4AAH///gP8AAH///gP8AAH///gP+AAH///gD/AAH/A/AB/AAH4A/AA/gAH4A+AAfgAH4B+AAfgAH4B+AAfgAH4B8AAfgAH4B8AAfgAH4B+AAfgAH4B+AAfgAH4B+AA/gAH4B/AA/AAH4A/gD/AAH4A/4H+AAH4Af//+AAH4AP//8AAH4AP//4AAHwAD//wAAAAAB//AAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAD////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gP4H+AAD/AfgD/AAH8A/AB/AAH8A/AA/gAH4B+AAfgAH4B+AAfgAPwB8AAfgAPwB8AAfgAPwB+AAfgAPwB+AAfgAH4B+AAfgAH4B/AA/gAH8B/AB/AAH+A/wD/AAD+A/8P+AAB8Af//+AAB4AP//8AAAwAH//4AAAAAD//gAAAAAA//AAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAHAAPwAAAA/AAPwAAAD/AAPwAAAf/AAPwAAB//AAPwAAP//AAPwAA//8AAPwAH//wAAPwAf/+AAAPwB//4AAAPwP//AAAAPw//8AAAAP3//gAAAAP//+AAAAAP//wAAAAAP//AAAAAAP/4AAAAAAP/gAAAAAAP+AAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAH+A//gAAAf/h//4AAA//z//8AAB/////+AAD/////+AAD///+H/AAH+H/4B/AAH8B/wA/gAH4A/gAfgAH4A/gAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAH4A/gAfgAH4A/gAfgAH8B/wA/gAH/H/4B/AAD///+H/AAD/////+AAB/////+AAA//z//8AAAf/h//4AAAH+A//gAAAAAAH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAD/8AAAAAAP/+AAAAAAf//AAcAAA///gA8AAB///wB+AAD/x/4B/AAD+AP4B/AAH8AH8A/gAH4AH8A/gAH4AD8AfgAP4AD8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAH4AD8AfgAH4AD4A/gAH8AH4B/AAD+APwD/AAD/g/wP+AAB/////+AAA/////8AAAf////4AAAP////wAAAH////AAAAA///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DxcjFyAfISAiHCAiEg=="), 54+(scale<<8)+(1<<16)); this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAB/AAAAAAAP/AAAAAAD//AAAAAA///AAAAAP///AAAAB///8AAAAf///AAAAH///wAAAB///+AAAAH///gAAAAH//4AAAAAH/+AAAAAAH/wAAAAAAH8AAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAH////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gAAH+AAD+AAAD/AAH8AAAB/AAH4AAAA/gAH4AAAAfgAH4AAAAfgAPwAAAAfgAPwAAAAfgAPwAAAAfgAHwAAAAfgAH4AAAAfgAH4AAAA/gAH8AAAA/AAD+AAAD/AAD/gAAH/AAB/////+AAB/////8AAA/////4AAAf////wAAAH////gAAAB///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAAfwAAAAAAA/gAAAAAAA/AAAAAAAB/AAAAAAAD+AAAAAAAD8AAAAAAAH8AAAAAAAH//////AAH//////AAH//////AAH//////AAH//////AAH//////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAA/AAAP4AAB/AAAf4AAD/AAA/4AAD/AAB/4AAH/AAD/4AAP/AAH/AAAf/AAH8AAA//AAH4AAB//AAP4AAD//AAPwAAH+/AAPwAAP8/AAPwAAf4/AAPwAA/4/AAPwAA/w/AAPwAB/g/AAPwAD/A/AAP4AH+A/AAH8AP8A/AAH/A/4A/AAD///wA/AAD///gA/AAB///AA/AAA//+AA/AAAP/8AA/AAAD/wAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAH4AAAHwAAH4AAAH4AAH4AAAH8AAH4AAAP+AAH4AAAH+AAH4A4AB/AAH4A+AA/AAH4B/AA/gAH4D/AAfgAH4H+AAfgAH4P+AAfgAH4f+AAfgAH4/+AAfgAH5/+AAfgAH5//AAfgAH7+/AA/gAH/8/gB/AAH/4f4H/AAH/wf//+AAH/gP//8AAH/AH//8AAH+AD//wAAH8AB//gAAD4AAf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAD/AAAAAAAP/AAAAAAB//AAAAAAH//AAAAAAf//AAAAAB///AAAAAH///AAAAAf/8/AAAAB//w/AAAAH/+A/AAAA//4A/AAAD//gA/AAAH/+AA/AAAH/4AA/AAAH/gAA/AAAH+AAA/AAAHwAAA/AAAHAAf///AAEAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAf///AAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAP/AHgAAH///AP4AAH///gP8AAH///gP8AAH///gP+AAH///gD/AAH/A/AB/AAH4A/AA/gAH4A+AAfgAH4B+AAfgAH4B+AAfgAH4B8AAfgAH4B8AAfgAH4B+AAfgAH4B+AAfgAH4B+AA/gAH4B/AA/AAH4A/gD/AAH4A/4H+AAH4Af//+AAH4AP//8AAH4AP//4AAHwAD//wAAAAAB//AAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///8AAAAD////AAAAP////wAAAf////4AAA/////8AAB/////+AAD/gP4H+AAD/AfgD/AAH8A/AB/AAH8A/AA/gAH4B+AAfgAH4B+AAfgAPwB8AAfgAPwB8AAfgAPwB+AAfgAPwB+AAfgAH4B+AAfgAH4B/AA/gAH8B/AB/AAH+A/wD/AAD+A/8P+AAB8Af//+AAB4AP//8AAAwAH//4AAAAAD//gAAAAAA//AAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAAAAPwAAAAHAAPwAAAA/AAPwAAAD/AAPwAAAf/AAPwAAB//AAPwAAP//AAPwAA//8AAPwAH//wAAPwAf/+AAAPwB//4AAAPwP//AAAAPw//8AAAAP3//gAAAAP//+AAAAAP//wAAAAAP//AAAAAAP/4AAAAAAP/gAAAAAAP+AAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAAAH+A//gAAAf/h//4AAA//z//8AAB/////+AAD/////+AAD///+H/AAH+H/4B/AAH8B/wA/gAH4A/gAfgAH4A/gAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAPwA/AAfgAH4A/gAfgAH4A/gAfgAH8B/wA/gAH/H/4B/AAD///+H/AAD/////+AAB/////+AAA//z//8AAAf/h//4AAAH+A//gAAAAAAH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAAD/8AAAAAAP/+AAAAAAf//AAcAAA///gA8AAB///wB+AAD/x/4B/AAD+AP4B/AAH8AH8A/gAH4AH8A/gAH4AD8AfgAP4AD8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAPwAB8AfgAH4AD8AfgAH4AD4A/gAH8AH4B/AAD+APwD/AAD/g/wP+AAB/////+AAA/////8AAAf////4AAAP////wAAAH////AAAAA///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAD8APwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DxcjFyAfISAiHCAiEg=="), 54+(scale<<8)+(1<<16));
return this; return this;
}; };
Graphics.prototype.setSmallFont = function(scale) { Graphics.prototype.setSmallFont = function(scale) {
// Actual height 28 (27 - 0) // Actual height 28 (27 - 0)
this.setFontCustom( this.setFontCustom(
@ -64,7 +63,6 @@ Graphics.prototype.setSmallFont = function(scale) {
return this; return this;
}; };
Graphics.prototype.setMiniFont = function(scale) { Graphics.prototype.setMiniFont = function(scale) {
// Actual height 16 (15 - 0) // Actual height 16 (15 - 0)
this.setFontCustom( this.setFontCustom(
@ -76,8 +74,6 @@ Graphics.prototype.setMiniFont = function(scale) {
return this; return this;
}; };
function imgLock(){ function imgLock(){
return { return {
width : 16, height : 16, bpp : 1, width : 16, height : 16, bpp : 1,
@ -86,434 +82,98 @@ function imgLock(){
} }
} }
function imgSteps(){
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/H///wv4CBn4CD8ACCj4IBj8f+Eeh/wjgCBngCCg/4nEH//4h/+jEP/gRBAQX+jkf/wgB//8GwP4FoICDHgICCBwIA=="))
}
}
function imgBattery(){ /************************************************
return { * Menu
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AN4EAg4TBgd///9oEAAQv8ARQRDDQQgCEwQ4OA"))
}
}
function imgCharging() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("//+v///k///4AQPwBANgBoMxBoMb/P+h/w/kH8H4gfB+EBwfggHH4EAt4CBn4CBj4CBh4FCCIO/8EB//Agf/wEH/8Gh//x////fAQIA="))
}
}
function imgBpm() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AOn4CD/wCCjgCCv/8jF/wGYgOA5MB//BC4PDAQnjAQPnAQgANA"))
}
}
function imgTemperature() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("//D///wICBjACBngCNkgCP/0kv/+s1//nDn/8wICEBAIOC/08v//IYJECA=="))
}
}
function imgWeather(){
return {
width : 24, height : 24, bpp : 1,
transparent : 0,
buffer : require("heatshrink").decompress(atob("AAcYAQ0MgEwAQUAngLB/8AgP/wACCgf/4Fz//OAQQICCIoaCEAQpGHA4ACA="))
}
}
function imgWind () {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/0f//8h///Pn//zAQXzwf/88B//mvGAh18gEevn/DIICB/PwgEBAQMHBAIADFwM/wEAGAP/54CD84CE+eP//wIQU/A=="))
}
}
function imgHumidity () {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("//7///+YCB+ICB8ACE4F/AQX9AQP54H//AOB+F/34CBj/gn8f4E+h/Aj0H4Ecg+AjED4ACE8E4gfwvEDEgICB/kHGwMP"))
}
}
function imgTimer() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/+B/4CD84CEBAPygFP+F+h/x/+P+fz5/n+HnAQNn5/wuYCBmYCC5kAAQfOgFz80As/ngHn+fD54mC/F+j/+gF/HAQA=="))
}
}
function imgWatch() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/8B//+ARANB/l4//5/1/+f/n/n5+fAQnf9/P44CC8/n7/n+YOB/+fDQQgCEwQsCHBBEC"))
}
}
function imgHomeAssistant() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AF84CB4YCBwICBCAP+jFH/k8g/4kkH+AFB8ACB4cY4eHzPhgmZkHnzPn8fb4/gvwUD8EYARhAC"))
}
}
function imgAgenda() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : require("heatshrink").decompress(atob("/4AFnPP+ALBAQX4CIgLFAQvggEBAQvAgEDAQMCwEAgwTBhgiB/AlCGQ8BGQQ"))
}
}
function imgMountain() {
return {
width : 24, height : 24, bpp : 1,
transparent : 1,
buffer : atob("//////////////////////3///n///D//uZ//E8//A+/+Z+f8//P5//n7//3z//zn//5AAAAAAAA////////////////////")
}
}
/************
* 2D MENU with entries of:
* [name, icon, opt[customDownFun], opt[customUpFun], opt[customCenterFun]]
*
*/ */
var menu = [ // Custom bwItems menu - therefore, its added here and not in a clkinfo.js file.
[ var bwItems = {
function(){ return [ null, null ] }, name: null,
function(){ return [ "Week " + weekOfYear(), null ] }, img: null,
], items: [
[ { name: "WeekOfYear",
function(){ return [ "Bangle", imgWatch() ] }, get: () => ({ text: "Week " + weekOfYear(), img: null}),
function(){ return [ E.getBattery() + "%", Bangle.isCharging() ? imgCharging() : imgBattery() ] }, show: function() { bwItems.items[0].emit("redraw"); },
function(){ return [ getSteps(), imgSteps() ] }, hide: function () {}
function(){ return [ Math.round(Bangle.getHealthStatus("last").bpm) + " bpm", imgBpm()] }, },
function(){ return [ measureAltitude, imgMountain() ]},
] ]
] };
/*
* Timer Menu
*/
try{
require('sched');
menu.push([
function(){
var text = isAlarmEnabled(TIMER_IDX) ? getAlarmMinutes(TIMER_IDX) + " min." : "Timer";
return [text, imgTimer(), () => decreaseAlarm(TIMER_IDX), () => increaseAlarm(TIMER_IDX), null ]
},
]);
} catch(ex) {
// If sched is not installed, we hide this menu item
}
/*
* AGENDA MENU
* Note that we handle the agenda differently in order to hide old entries...
*/
var agendaIdx = 0;
var agendaTimerIdx = 0;
if(storage.readJSON("android.calendar.json") !== undefined){
function nextAgendaEntry(){
agendaIdx += 1;
}
function previousAgendaEntry(){
agendaIdx -= 1;
}
menu.push([
function(){
var now = new Date();
var agenda = storage.readJSON("android.calendar.json")
.filter(ev=>ev.timestamp + ev.durationInSeconds > now/1000)
.sort((a,b)=>a.timestamp - b.timestamp);
if(agenda.length <= 0){
return ["All done", imgAgenda()]
}
agendaIdx = agendaIdx < 0 ? 0 : agendaIdx;
agendaIdx = agendaIdx >= agenda.length ? agendaIdx -1 : agendaIdx;
var entry = agenda[agendaIdx];
var title = entry.title.slice(0,14);
var date = new Date(entry.timestamp*1000);
var dateStr = locale.date(date).replace(/\d\d\d\d/,"");
dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : "";
function dynImgAgenda(){
if(isAlarmEnabled(TIMER_AGENDA_IDX) && agendaTimerIdx == agendaIdx){
return imgTimer();
} else {
return imgAgenda();
}
}
return [title + "\n" + dateStr, dynImgAgenda(), () => nextAgendaEntry(), () => previousAgendaEntry(), function(){
try{
var alarm = require('sched')
// If other time, we disable the old one and enable this one.
if(agendaIdx != agendaTimerIdx){
agendaTimerIdx = -1;
alarm.setAlarm(TIMER_AGENDA_IDX, undefined);
}
// Disable alarm if enabled
if(isAlarmEnabled(TIMER_AGENDA_IDX)){
agendaTimerIdx = -1;
alarm.setAlarm(TIMER_AGENDA_IDX, undefined);
alarm.reload();
return
}
// Otherwise, set alarm for given event
agendaTimerIdx = agendaIdx;
alarm.setAlarm(TIMER_AGENDA_IDX, {
msg: title,
timer : parseInt((date - now)),
});
alarm.reload();
} catch(ex){ }
}]
},
]);
}
/*
* WEATHER MENU
*/
if(storage.readJSON('weather.json') !== undefined){
menu.push([
function(){ return [ "Weather", imgWeather() ] },
function(){ return [ getWeather().temp, imgTemperature() ] },
function(){ return [ getWeather().hum, imgHumidity() ] },
function(){ return [ getWeather().wind, imgWind() ] },
]);
}
/*
* HOME ASSISTANT MENU
*/
try{
var triggers = require("ha.lib.js").getTriggers();
var haMenu = [
function(){ return [ "Home", imgHomeAssistant() ] },
];
triggers.forEach(trigger => {
haMenu.push(function(){
return [trigger.display, trigger.getIcon(), () => {}, () => {}, function(){
var ha = require("ha.lib.js");
ha.sendTrigger("TRIGGER_BW");
ha.sendTrigger(trigger.trigger);
}]
});
})
menu.push(haMenu);
} catch(ex){
// If HomeAssistant is not installed, we hide this item
}
function getMenuEntry(){
// In case the user removes HomeAssistant entries, showInfo
// could be larger than infoArray.length...
settings.menuPosX = settings.menuPosX % menu.length;
settings.menuPosY = settings.menuPosY % menu[settings.menuPosX].length;
var menuEntry = menu[settings.menuPosX][settings.menuPosY]();
if(menuEntry[0] == null){
return menuEntry;
}
// For the first entry we always convert it into a callback function
// such that the menu is compatible with async functions such as
// measuring the pressure, altitude or sending http requests...
if(typeof menuEntry[0] !== 'function'){
var value = menuEntry[0];
menuEntry[0] = function(callbackFun){
callbackFun(String(value), settings.menuPosX, settings.menuPosY);
}
}
return menuEntry;
}
/************
* Helper
*/
function isFullscreen(){
var s = settings.screen.toLowerCase();
if(s == "dynamic"){
return Bangle.isLocked()
} else {
return s == "full"
}
}
function getSteps() {
var steps = 0;
try{
if (WIDGETS.wpedom !== undefined) {
steps = WIDGETS.wpedom.getSteps();
} else if (WIDGETS.activepedom !== undefined) {
steps = WIDGETS.activepedom.getSteps();
} else {
steps = Bangle.getHealthStatus("day").steps;
}
} catch(ex) {
// In case we failed, we can only show 0 steps.
}
return steps;
}
function getWeather(){
var weatherJson;
try {
weatherJson = storage.readJSON('weather.json');
var weather = weatherJson.weather;
// Temperature
weather.temp = locale.temp(weather.temp-273.15);
// Humidity
weather.hum = weather.hum + "%";
// Wind
const wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
weather.wind = Math.round(wind[1]) + "kph";
return weather
} catch(ex) {
// Return default
}
return {
temp: " ? ",
hum: " ? ",
txt: " ? ",
wind: " ? ",
wdir: " ? ",
wrose: " ? "
};
}
// From https://weeknumber.com/how-to/javascript
function weekOfYear() { function weekOfYear() {
var date = new Date(); var date = new Date();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
// Thursday in current week decides the year. // Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
// January 4 is always in week 1. // January 4 is always in week 1.
var week1 = new Date(date.getFullYear(), 0, 4); var week1 = new Date(date.getFullYear(), 0, 4);
// Adjust to Thursday in week 1 and count number of weeks from date to week1. // Adjust to Thursday in week 1 and count number of weeks from date to week1.
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
- 3 + (week1.getDay() + 6) % 7) / 7); - 3 + (week1.getDay() + 6) % 7) / 7);
} }
function isAlarmEnabled(idx){ // Load menu
try{ var menu = clock_info.load();
var alarm = require('sched'); menu = menu.concat(bwItems);
var alarmObj = alarm.getAlarm(idx);
if(alarmObj===undefined || !alarmObj.on){
return false; // 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);
} }
return true; item.on('redraw', drawItem);
})
} catch(ex){ } });
return false;
}
function getAlarmMinutes(idx){ function canRunMenuItem(){
if(!isAlarmEnabled(idx)){ if(settings.menuPosY == 0){
return -1; return false;
} }
var alarm = require('sched'); var menuEntry = menu[settings.menuPosX];
var alarmObj = alarm.getAlarm(idx); var item = menuEntry.items[settings.menuPosY-1];
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); return item.run !== undefined;
} }
function increaseAlarm(idx){ function runMenuItem(){
if(settings.menuPosY == 0){
return;
}
var menuEntry = menu[settings.menuPosX];
var item = menuEntry.items[settings.menuPosY-1];
try{ try{
var minutes = isAlarmEnabled(idx) ? getAlarmMinutes(idx) : 0; item.run();
var alarm = require('sched'); Bangle.buzz(300, 0.6).then(() => {});
alarm.setAlarm(idx, { } catch (ex) {
timer : (minutes+5)*60*1000, // Simply ignore it...
});
alarm.reload();
} catch(ex){ }
}
function decreaseAlarm(idx){
try{
var minutes = getAlarmMinutes(idx);
minutes -= 5;
var alarm = require('sched')
alarm.setAlarm(idx, undefined);
if(minutes > 0){
alarm.setAlarm(idx, {
timer : minutes*60*1000,
});
}
alarm.reload();
} catch(ex){ }
}
function measureAltitude(callbackFun){
var oldX = settings.menuPosX;
var oldY = settings.menuPosY;
try{
Bangle.getPressure().then(data=>{
if(data && data.altitude && data.altitude > -100){
callbackFun(Math.round(data.altitude) + "m", oldX, oldY);
} else {
callbackFun("???", oldX, oldY);
}
});
}catch(ex){
callbackFun("err", oldX, oldY);
} }
} }
/************ /************************************************
* DRAW * Draw
*/ */
function draw() { function draw() {
// Queue draw again // Queue draw again
@ -521,7 +181,7 @@ function draw() {
// Draw clock // Draw clock
drawDate(); drawDate();
drawTime(); drawMenuAndTime();
drawLock(); drawLock();
drawWidgets(); drawWidgets();
} }
@ -529,12 +189,12 @@ function draw() {
function drawDate(){ function drawDate(){
// Draw background // Draw background
var y = H/5*2; var y = H/5*2 + (isFullscreen() ? 0 : 8);
g.reset().clearRect(0,0,W,W); g.reset().clearRect(0,0,W,y);
// Draw date // Draw date
y = parseInt(y/2)+4; y = parseInt(y/2)+4;
y += isFullscreen() ? 0 : 13; y += isFullscreen() ? 0 : 8;
var date = new Date(); var date = new Date();
var dateStr = date.getDate(); var dateStr = date.getDate();
dateStr = ("0" + dateStr).substr(-2); dateStr = ("0" + dateStr).substr(-2);
@ -557,11 +217,8 @@ function drawDate(){
} }
function drawTime(){ function drawTime(y, smallText){
// Draw background // Draw background
var y = H/5*2 + (isFullscreen() ? 0 : 8);
g.setColor(g.theme.fg);
g.fillRect(0,y,W,H);
var date = new Date(); var date = new Date();
// Draw time // Draw time
@ -577,56 +234,65 @@ function drawTime(){
// Set y coordinates correctly // Set y coordinates correctly
y += parseInt((H - y)/2) + 5; y += parseInt((H - y)/2) + 5;
var menuEntry = getMenuEntry();
var menuTextFun = menuEntry[0];
var menuImg = menuEntry[1];
var printImgLeft = settings.menuPosY != 0;
// Show large or small time depending on info entry // Show large or small time depending on info entry
if(menuTextFun == null){ if(smallText){
g.setLargeFont();
g.drawString(timeStr, W/2, y);
return;
} else {
y -= 15; y -= 15;
g.setMediumFont(); g.setMediumFont();
g.drawString(timeStr, W/2, y); } else {
g.setLargeFont();
} }
g.drawString(timeStr, W/2, y);
}
// Async set the menu (could be that some data is async fetched) function drawMenuItem(text, image){
menuTextFun((menuText, oldX, oldY) => { // First clear the time region
var y = H/5*2 + (isFullscreen() ? 0 : 8);
// We display the text IFF the user did not change the menu g.setColor(g.theme.fg);
if(settings.menuPosX != oldX || settings.menuPosY != oldY){ g.fillRect(0,y,W,H);
return;
}
// As its a callback, we have to ensure that the color // Draw menu text
// font etc. is still correct... var hasText = (text != null && text != "");
g.setColor(g.theme.bg); if(hasText){
g.setFontAlign(0,0); g.setFontAlign(0,0);
y += 35;
if(menuText.split('\n').length > 1){ // For multiline text we show an even smaller font...
text = String(text);
if(text.split('\n').length > 1){
g.setMiniFont(); g.setMiniFont();
} else { } else {
g.setSmallFont(); g.setSmallFont();
} }
var imgWidth = 0; var imgWidth = image == null ? 0 : 24;
if(menuImg){ var strWidth = g.stringWidth(text);
imgWidth = 24.0; g.setColor(g.theme.fg).fillRect(0, 149-14, W, H);
var strWidth = g.stringWidth(menuText); g.setColor(g.theme.bg).drawString(text, W/2 + imgWidth/2 + 2, 149+3);
var scale = imgWidth / menuImg.width;
g.drawImage( if(image != null){
menuImg, var scale = imgWidth / image.width;
W/2 + (printImgLeft ? -strWidth/2-4 : strWidth/2+4) - parseInt(imgWidth/2), g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 149 - parseInt(imgWidth/2), {scale: scale});
y - parseInt(imgWidth/2),
{ scale: scale }
);
} }
g.drawString(menuText, printImgLeft ? W/2 + imgWidth/2 + 2 : W/2 - imgWidth/2 - 2, y+3); }
});
// Draw time
drawTime(y, hasText);
}
function drawMenuAndTime(){
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();
} }
@ -647,9 +313,19 @@ function drawWidgets(){
} }
function isFullscreen(){
var s = settings.screen.toLowerCase();
if(s == "dynamic"){
return Bangle.isLocked()
} else {
return s == "full"
}
}
/*
* Draw timeout
/************************************************
* Listener
*/ */
// timeout used to update every minute // timeout used to update every minute
var drawTimeout; var drawTimeout;
@ -692,7 +368,7 @@ Bangle.on('charging',function(charging) {
drawTimeout = undefined; drawTimeout = undefined;
// Jump to battery // Jump to battery
settings.menuPosX = 1; settings.menuPosX = 0;
settings.menuPosY = 1; settings.menuPosY = 1;
draw(); draw();
}); });
@ -710,17 +386,15 @@ Bangle.on('touch', function(btn, e){
var is_right = e.x > right && !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; var is_center = !is_upper && !is_lower && !is_left && !is_right;
if(lock_input){
return;
}
if(is_lower){ if(is_lower){
Bangle.buzz(40, 0.6); Bangle.buzz(40, 0.6);
settings.menuPosY = (settings.menuPosY+1) % menu[settings.menuPosX].length; settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1);
// Handle custom menu entry function drawMenuAndTime();
var menuEntry = getMenuEntry();
if(menuEntry.length > 2){
menuEntry[2]();
}
drawTime();
} }
if(is_upper){ if(is_upper){
@ -730,53 +404,29 @@ Bangle.on('touch', function(btn, e){
Bangle.buzz(40, 0.6); Bangle.buzz(40, 0.6);
settings.menuPosY = settings.menuPosY-1; settings.menuPosY = settings.menuPosY-1;
settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].length-1 : settings.menuPosY; settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY;
// Handle custom menu entry function drawMenuAndTime();
var menuEntry = getMenuEntry();
if(menuEntry.length > 3){
menuEntry[3]();
}
drawTime();
} }
if(is_right){ if(is_right){
// A bit hacky but we ensure that always the first agenda entry is shown...
agendaIdx = 0;
Bangle.buzz(40, 0.6); Bangle.buzz(40, 0.6);
settings.menuPosX = (settings.menuPosX+1) % menu.length; settings.menuPosX = (settings.menuPosX+1) % menu.length;
settings.menuPosY = 0; settings.menuPosY = 0;
drawTime(); drawMenuAndTime();
} }
if(is_left){ if(is_left){
// A bit hacky but we ensure that always the first agenda entry is shown...
agendaIdx = 0;
Bangle.buzz(40, 0.6); Bangle.buzz(40, 0.6);
settings.menuPosY = 0; settings.menuPosY = 0;
settings.menuPosX = settings.menuPosX-1; settings.menuPosX = settings.menuPosX-1;
settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX; settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX;
drawTime(); drawMenuAndTime();
} }
if(is_center){ if(is_center){
var menuEntry = getMenuEntry(); if(canRunMenuItem()){
if(menuEntry.length > 4 && menuEntry[4] != null){ runMenuItem();
Bangle.buzz(80, 0.6).then(()=>{
try{
menuEntry[4]();
setTimeout(()=>{
Bangle.buzz(80, 0.6);
drawTime();
}, 250);
} catch(ex){
// In case it fails, we simply ignore it.
}
}
);
} }
} }
}); });
@ -791,9 +441,10 @@ E.on("kill", function(){
}); });
/* /************************************************
* Draw clock the first time * Startup Clock
*/ */
// The upper part is inverse i.e. light if dark and dark if light theme // The upper part is inverse i.e. light if dark and dark if light theme
// is enabled. In order to draw the widgets correctly, we invert the // is enabled. In order to draw the widgets correctly, we invert the
// dark/light theme as well as the colors. // dark/light theme as well as the colors.

View File

@ -1,7 +1,7 @@
{ {
"id": "bwclk", "id": "bwclk",
"name": "BW Clock", "name": "BW Clock",
"version": "0.21", "version": "0.22",
"description": "A very minimalistic clock to mainly show date and time.", "description": "A very minimalistic clock to mainly show date and time.",
"readme": "README.md", "readme": "README.md",
"icon": "app.png", "icon": "app.png",

View File

@ -1,2 +1,3 @@
0.01: Release 0.01: Release
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.

25
apps/ha/ha.clkinfo.js Normal file
View File

@ -0,0 +1,25 @@
(function() {
var ha = require("ha.lib.js");
var triggers = ha.getTriggers();
var haItems = {
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=="),
items: []
};
triggers.forEach((trigger, i) => {
haItems.items.push({
name: "haTrigger-" + i,
get: () => ({ text: trigger.display, img: trigger.getIcon()}),
show: function() { haItems.items[i].emit("redraw"); },
hide: function () {},
run: function() {
ha.sendTrigger("TRIGGER_BW");
ha.sendTrigger(trigger.trigger);
}
});
});
return haItems;
})

View File

@ -1,7 +1,7 @@
{ {
"id": "ha", "id": "ha",
"name": "HomeAssistant", "name": "HomeAssistant",
"version": "0.02", "version": "0.03",
"description": "Integrates your BangleJS into HomeAssistant.", "description": "Integrates your BangleJS into HomeAssistant.",
"icon": "ha.png", "icon": "ha.png",
"type": "app", "type": "app",
@ -20,6 +20,7 @@
"storage": [ "storage": [
{"name":"ha.app.js","url":"ha.app.js"}, {"name":"ha.app.js","url":"ha.app.js"},
{"name":"ha.lib.js","url":"ha.lib.js"}, {"name":"ha.lib.js","url":"ha.lib.js"},
{"name":"ha.clkinfo.js","url":"ha.clkinfo.js"},
{"name":"ha.img","url":"ha.icon.js","evaluate":true} {"name":"ha.img","url":"ha.icon.js","evaluate":true}
] ]
} }

View File

@ -13,3 +13,4 @@
0.14: Use weather condition code for icon selection 0.14: Use weather condition code for icon selection
0.15: Fix widget icon 0.15: Fix widget icon
0.16: Don't mark app as clock 0.16: Don't mark app as clock
0.17: Added clkinfo for clocks.

43
apps/weather/clkinfo.js Normal file
View File

@ -0,0 +1,43 @@
(function() {
var weather = {
temp: "?",
hum: "?",
wind: "?",
};
var weatherJson = storage.readJSON('weather.json');
if(weatherJson !== undefined && weatherJson.weather !== undefined){
weather = weatherJson.weather;
weather.temp = locale.temp(weather.temp-273.15);
weather.hum = weather.hum + "%";
weather.wind = locale.speed(weather.wind).match(/^(\D*\d*)(.*)$/);
weather.wind = Math.round(weather.wind[1]) + "kph";
}
var weatherItems = {
name: "Weather",
img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="),
items: [
{
name: "temperature",
get: () => ({ text: weather.temp, img: atob("GBiBAf/D//+B//8Y//88//88//88//88//88//8k//8k//8k//8k//8k//8k//4kf/5mf/zDP/yBP/yBP/zDP/5mf/48f/8A///D/w==")}),
show: function() { weatherItems.items[0].emit("redraw"); },
hide: function () {}
},
{
name: "humidity",
get: () => ({ text: weather.hum, img: atob("GBiBAf/7///z///x///g///g///Af//Af/3Af/nA//jg//B/v/B/H+A/H8A+D8AeB8AcB4AYA8AYA8AYA+A4A/B4A//4A//8B///Dw==")}),
show: function() { weatherItems.items[1].emit("redraw"); },
hide: function () {}
},
{
name: "wind",
get: () => ({ text: weather.wind, img: atob("GBiBAf4f//wP//nn//Pn//Pzg//nAf/meIAOfAAefP///P//+fAAAfAAB////////wAAP4AAH///z///z//nz//nz//zj//wH//8Pw==")}),
show: function() { weatherItems.items[2].emit("redraw"); },
hide: function () {}
},
]
};
return weatherItems;
})

View File

@ -1,7 +1,7 @@
{ {
"id": "weather", "id": "weather",
"name": "Weather", "name": "Weather",
"version": "0.16", "version": "0.17",
"description": "Show Gadgetbridge weather report", "description": "Show Gadgetbridge weather report",
"icon": "icon.png", "icon": "icon.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],
@ -13,7 +13,8 @@
{"name":"weather.wid.js","url":"widget.js"}, {"name":"weather.wid.js","url":"widget.js"},
{"name":"weather","url":"lib.js"}, {"name":"weather","url":"lib.js"},
{"name":"weather.img","url":"icon.js","evaluate":true}, {"name":"weather.img","url":"icon.js","evaluate":true},
{"name":"weather.settings.js","url":"settings.js"} {"name":"weather.settings.js","url":"settings.js"},
{"name":"weather.clkinfo.js","url":"clkinfo.js"}
], ],
"data": [{"name":"weather.json"}] "data": [{"name":"weather.json"}]
} }

View File

@ -2,9 +2,14 @@ var exports = {};
/* Module that allows for loading of clock 'info' displays /* Module that allows for loading of clock 'info' displays
that can be scrolled through on the clock face. that can be scrolled through on the clock face.
`load()` returns an array of menu items: `load()` returns an array of menu objects, where each object contains a list of menu items:
* 'name' : text to display and identify menu object (e.g. weather)
* 'img' : a 24x24px image
* 'items' : menu items such as temperature, humidity, wind etc.
* 'item.name' : friendly name Note that each item is an object with:
* 'item.name' : friendly name to identify an item (e.g. temperature)
* 'item.get' : function that resolves with: * 'item.get' : function that resolves with:
{ {
'text' : the text to display for this item 'text' : the text to display for this item
@ -20,14 +25,18 @@ See the bottom of this file for example usage...
example.clkinfo.js : example.clkinfo.js :
(function() { (function() {
return [ return {
{ name : "Example", name: "Bangle",
get : () => ({ text : "Bangle.js", img: atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==") }),
img : atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==") }), items: [
show : () => {}, { name : "Item1",
hide : () => {} get : () => ({ text : "TextOfItem1",
} img : atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA==") }),
]; show : () => {},
hide : () => {}
}
]
};
}) // must not have a semi-colon! }) // must not have a semi-colon!
*/ */
@ -38,65 +47,72 @@ exports.load = function() {
var hrm = "--"; var hrm = "--";
var alt = "--"; var alt = "--";
// callbacks (needed for easy removal of listeners) // callbacks (needed for easy removal of listeners)
function batteryUpdateHandler() { items[0].emit("redraw"); } function batteryUpdateHandler() { bangleItems[0].emit("redraw"); }
function stepUpdateHandler() { items[1].emit("redraw"); } function stepUpdateHandler() { bangleItems[1].emit("redraw"); }
function hrmUpdateHandler() { items[2].emit("redraw"); } function hrmUpdateHandler() { bangleItems[2].emit("redraw"); }
function altUpdateHandler() { function altUpdateHandler() {
Bangle.getPressure().then(data=>{ Bangle.getPressure().then(data=>{
if (!data) return; if (!data) return;
alt = Math.round(data.altitude) + "m"; alt = Math.round(data.altitude) + "m";
items[3].emit("redraw"); bangleItems[3].emit("redraw");
}); });
} }
// actual items // actual menu
var items = [ var menu = [{
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=="),
items: [
{ name : "Battery", { name : "Battery",
get : () => ({ get : () => ({
text : E.getBattery() + "%", text : E.getBattery() + "%",
img : atob(Bangle.isCharging() ? "GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==" : "GBiBAAAAAAAAAAAAAAAAAAAAAD//+P///IAAAr//Ar//Ar//A7//A7//A7//A7//Ar//AoAAAv///D//+AAAAAAAAAAAAAAAAAAAAA==") }), img : atob(Bangle.isCharging() ? "GBiBAAABgAADwAAHwAAPgACfAAHOAAPkBgHwDwP4Hwf8Pg/+fB//OD//kD//wD//4D//8D//4B//QB/+AD/8AH/4APnwAHAAACAAAA==" : "GBiBAAAAAAAAAAAAAAAAAAAAAD//+P///IAAAr//Ar//Ar//A7//A7//A7//A7//Ar//AoAAAv///D//+AAAAAAAAAAAAAAAAAAAAA==") }),
show : function() { show : function() { this.interval = setInterval(()=>this.emit('redraw'), 60000); Bangle.on("charging", batteryUpdateHandler); batteryUpdateHandler(); },
this.interval = setInterval(()=>this.emit('redraw'), 60000); hide : function() { clearInterval(this.interval); delete this.interval; Bangle.removeListener("charging", batteryUpdateHandler); },
Bangle.on("charging", batteryUpdateHandler);
},
hide : function() {
clearInterval(this.interval);
delete this.interval;
Bangle.removeListener("charging", batteryUpdateHandler);
},
}, },
{ name : "Steps", get : () => ({ { name : "Steps", get : () => ({
text : Bangle.getHealthStatus("day").steps, text : Bangle.getHealthStatus("day").steps,
img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==") }), img : atob("GBiBAAcAAA+AAA/AAA/AAB/AAB/gAA/g4A/h8A/j8A/D8A/D+AfH+AAH8AHn8APj8APj8AHj4AHg4AADAAAHwAAHwAAHgAAHgAADAA==") }),
show : function() { Bangle.on("step", stepUpdateHandler); }, show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); },
hide : function() { Bangle.removeListener("step", stepUpdateHandler); }, hide : function() { Bangle.removeListener("step", stepUpdateHandler); },
}, },
{ name : "HRM", get : () => ({ { name : "HRM", get : () => ({
text : Math.round(Bangle.getHealthStatus("last").bpm) + " bpm", text : Math.round(Bangle.getHealthStatus("last").bpm) + " bpm",
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==") }), img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAADAAADAAAHAAAHjAAHjgAPngH9n/n82/gA+AAA8AAA8AAAcAAAYAAAYAAAAAAAAAAAAAAAAAAAAAA==") }),
show : function() { Bangle.setHRMPower(1,"clkinfo"); Bangle.on("HRM", hrmUpdateHandler); hrm = Math.round(Bangle.getHealthStatus("last").bpm); }, show : function() { Bangle.setHRMPower(1,"clkinfo"); Bangle.on("HRM", hrmUpdateHandler); hrm = Math.round(Bangle.getHealthStatus("last").bpm); hrmUpdateHandler(); },
hide : function() { Bangle.setHRMPower(0,"clkinfo"); Bangle.removeListener("HRM", hrmUpdateHandler); hrm = "--"; }, hide : function() { Bangle.setHRMPower(0,"clkinfo"); Bangle.removeListener("HRM", hrmUpdateHandler); hrm = "--"; },
} }
]; ],
if (Bangle.getPressure) // Altimeter may not exist }];
items.push({ name : "Altitude", get : () => ({ var bangleItems = menu[0].items;
if (Bangle.getPressure){ // Altimeter may not exist
bangleItems.push({ name : "Altitude", get : () => ({
text : alt, text : alt,
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==") }), img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==") }),
show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); }, show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); },
hide : function() { clearInterval(this.interval); delete this.interval; }, hide : function() { clearInterval(this.interval); delete this.interval; },
}); });
// now load extra data from a third party files }
// In case there exists already a menu object b with the same name as the next
// object a, we append the items. Otherwise we add the new object a to the list.
require("Storage").list(/clkinfo.js$/).forEach(fn => { require("Storage").list(/clkinfo.js$/).forEach(fn => {
items = items.concat(eval(require("Storage").read(fn))()); var a = eval(require("Storage").read(fn))();
var b = menu.find(x => x.name === a.name)
if(b) b.items = b.items.concat(a.items);
else menu = menu.concat(a);
}); });
// return it all! // return it all!
return items; return menu;
}; };
// Code for testing // Code for testing
/* /*
g.clear(); g.clear();
var items = exports.load(); // or require("clock_info").load() var menu = exports.load(); // or require("clock_info").load()
var itemsFirstMenu = menu[0].items;
items.forEach((itm,i) => { items.forEach((itm,i) => {
var y = i*24; var y = i*24;
console.log("Starting", itm.name); console.log("Starting", itm.name);