Bangle.js 应用加载器(及应用)
- 在 banglejs.com/apps 上试用 release version
- 在 espruino.github.io 上试用 development version
release version 会定期手动刷新,而 development version 会随着新代码提交到本仓库而持续更新。
本仓库中的所有软件(包括应用)均采用 MIT 许可证 - 参见 LICENSE 通过 向本仓库提交代码,您确认同意其采用 MIT 许可证, 并且不存在其他可能使此情况不可能的许可方式。
工作原理?
- 应用列表位于
apps.json(这是使用 Jekyll 或bin/create_apps_json.sh从所有apps/yourapp/metadata.json自动生成的) - 每个元素引用
apps/<id>中的一个应用,该应用会被上传 - 启动时,BangleAppLoader 会检查 JSON 并将其 与手表存储中看到的文件进行比较。
- 要上传应用,BangleAppLoader 会检查
apps.json中列出的文件,加载它们,并通过 Web Bluetooth 发送。
入门指南
查看:
使用的文件名
存储中的文件名限制为 28 个字符。为了 轻松区分文件类型,我们使用以下命名:
appid.info是描述应用的 JSON - 这是由 App Loader 自动生成的appid.img是一张图片appid.app.js是应用程序的 JS 代码appid.wid.js是 widgets 的 JS 代码appid.clkinfo.js是 clock info 的 JS 代码appid.settings.js是 the settings menu 的 JS 代码appid.boot.js是在启动时自动运行的 JS 代码appid.json用于应用的 JSON 设置
开发你自己的应用
- 前往 the Web IDE 并确保设置中的
Save on Send设置为 默认设置To RAM - 我们建议你先使用 'Example Applications'(下方)中的代码开始...
- 将
app.js或widget.js加载到 IDE 中并开始开发。 Upload按钮会将你的应用临时加载到 Bangle.js
将你的应用添加到菜单
- 想一个唯一的(全小写,无空格)名称,我们假设是
myappid。Bangle.js 文件名限制为 28 个字符,并会添加文件扩展名(例如.js),因此请 尽量保持文件名简短,以避免缓冲区溢出。 - 创建一个名为
apps/<id>的文件夹,假设是apps/myappid - 我们建议你从
apps/_example_*中的一个示例(见下文)复制文件,或者... apps/myappid/app.png应该是一个 48px 的图标- 使用 http://www.espruino.com/Image+Converter 创建
apps/myappid/app-icon.js,使用 1 位、4 位或 8 位 Web 调色板 "Image String" - 创建/修改
apps/myappid/metadata.json如下:
{ "id": "myappid",
"name": "My app's human readable name",
"shortName" : "Short Name",
"icon": "app.png",
"description": "A detailed description of my great app",
"tags": "",
"storage": [
{"name":"myappid.app.js","url":"app.js"},
{"name":"myappid.img","url":"app-icon.js","evaluate":true}
],
},
屏幕截图
在应用 metadata.json 文件中,你可以添加一个屏幕截图列表,使用类似如下的行:"screenshots" : [ { "url":"screenshot.png" } ],
要获取屏幕截图,你可以:
- 在连接到 Bangle.js 2 时,在 Web IDE 的左侧输入
g.dump()- 然后你可以 右键点击并保存终端中显示的图像(这仅在 Bangle.js 2 上有效 - Bangle.js 1 无法 从 LCD 控制器读回数据)。 - 在模拟器中运行你的代码,并使用窗口右下角的屏幕截图按钮。
你还可以截取多张屏幕截图,并使用像 this 这样的网站将其转换为动画 .gif 文件,并提供该文件以在应用中展示运动效果(闪烁的图像、动画等)。
测试
对于使用 Bangle.js 模拟器进行应用功能测试,请参阅 TESTING.md。
在线
这是测试的最佳方式...
- Fork https://github.com/espruino/BangleApps git 仓库
- 添加你的文件
- 前往 GitHub 设置并激活 GitHub Pages
- 运行你的个人
Bangle App Loader于 https://<your-github-username>.github.io/BangleApps/index.html 以将应用加载到你的设备上 - 你的应用应该在其中 - 如果出现问题,请检查你的 Web 浏览器的“开发者控制台”中的错误
注意: 在你的 PC 上获取该仓库的本地副本是个好主意,
然后运行 bin/sanitycheck.js - 它会检查一系列可能存在的常见问题。要在本地运行该项目,你必须先初始化并更新 git 子模块:git submodule update --init。
请注意提交与 github.io 上的更新之间的延迟——更改生效可能需要几分钟时间(并且需要对浏览器进行“强制刷新”)。
离线
使用 Web IDE (4 个磁盘)中的“存储”图标,将文件上传到 JSON 中描述的位置:
app-icon.js->myappid.img
现在在编辑器中加载 app.js,并点击 Send to Espruino 图标右下角的下拉箭头。点击 Storage,然后选择
myappid.app.js(如果你之前已上传过你的应用),或者选择 New File
然后输入 myappid.app.js 作为名称。
现在,点击 Send to Espruino 图标将直接把应用加载到
Espruino 并且 会自动运行它。
当你以这种方式上传代码时,你的应用甚至会被上传到 Bangle.js 的菜单中,
而无需你使用 Bangle App Loader
注意: 小部件需要在时钟或应用内部运行,因此如果你正在
开发一个小部件,你需要前往 Settings -> Communications -> Load after saving
并将其设置为 Load default application。
示例应用
为了简化流程,我们想出了几个示例应用,你可以在创建自己的应用时将其作为基础。只需想出一个独特的名称(理想情况下为小写,少于 20 个字符),将 apps/_example_app
或 apps/_example_widget 复制到 apps/myappid,并相应地编辑 apps/myappid/metadata.json。
注意: 文件名最大长度为 28 个字符,因此我们建议应用 ID 少于
20 个字符,这样当在末尾添加 .app.js/etc 时文件名不会被截断。
如果你正在制作一个小部件 请以 wid 开头命名,以便
轻松找到!
应用示例
应用示例可在 apps/_example_app 中找到
应用列在 Bangle.js 菜单中,可通过时钟应用中的中间按钮访问。
metadata.json- 向 bootloader 和 loader 描述该应用app.png- 应用图标 - 48x48pxapp-icon.js- 图标的 JS 版本(使用 http://www.espruino.com/Image+Converter 制作),用于 Bangle.js 的菜单app.js- 应用代码ChangeLog- 包含应用更改列表的文件,以便用户查看更改内容
app-icon.js
图标图像和简短描述用于 Bangle.js 的启动器。
使用 Espruino 图像转换器 并上传你的 app.png 文件。
按照以下步骤创建可读的图像字符串图标。
- 上传一个 48x48 的 png 文件 - 图像应为 48x48 或更小
- 设置 X 使用压缩
- 设置 X 透明度(可选)
- 设置扩散:flat
- 设置颜色:1 bit、任意优化选项或 8 bit Web Palette 最佳
- 设置输出为:Image String
用图像转换器的输出替换这一行:
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
不要添加尾部分号
您还可以使用此转换器来创建您希望用 g.drawImage() 在您的应用中绘制的图像。
需要小部件的应用程序可以在启动时调用 Bangle.loadWidgets() 一次 来加载
它们,然后调用 Bangle.drawWidgets() 在应用程序需要完全清除屏幕时将它们绘制到屏幕上。小部件本身会在需要时自行更新。
小部件示例
小部件示例可在 apps/_example_widget 中找到
metadata.json- 向引导加载程序和加载器描述小部件widget.js- 小部件代码
小部件只是运行在支持它们的应用程序调用 Bangle.loadWidgets() 时的一小段代码。如果它们想在屏幕顶部 24px 高
的小部件栏中显示某些内容,它们可以将自己添加到全局
WIDGETS 数组中:
WIDGETS["mywidget"]={
area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
sortorder:0, // (Optional) determines order of widgets in the same corner
width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
当小部件需要绘制时,x 和 y 的值会在 WIDGETS["mywidget"] 中设置,
然后 draw 可以使用 this.x 和 this.y 来确定需要绘制的位置。
ChangeLog
这是一个包含应用程序更改列表的文件,以便用户查看更改内容,例如:
0.01: New App!
0.02: Changed the colors
0.03: Made the app run quicker
条目应按最新条目置于末尾的顺序排列,且最后一个条目的版本号需与 metadata.json 中的版本一致。
请保持与示例相同的格式,因为该文件需要由 BangleApps 工具进行解析。
app.info 格式
此文件是从 metadata.json 自动生成 的,并由 App Loader 加载到 Bangle.js 上,
用于向 Launcher 提供有关该应用的信息。
{
"name":"Short Name", // for Bangle.js menu
"icon":"*myappid", // for Bangle.js menu
"src":"-myappid", // source file
"type":"widget/clock/app/bootloader/...", // optional, default "app"
// see 'type' in 'metadata.json format' below for more options/info
"version":"0.01",
// added by BangleApps loader on upload based on metadata.json
"files:"file1,file2,file3",
// added by BangleApps loader on upload - lists all files
// that belong to the app so it can be deleted
"data":"appid.data.json,appid.data?.json;appidStorageFile,appidStorageFile*"
// added by BangleApps loader on upload - lists files that
// the app might write, so they can be deleted on uninstall
// typically these files are not uploaded, but created by the app
// these can include '*' or '?' wildcards
}
metadata.json 格式
{ "id": "appid", // 7 character app id
"name": "Readable name", // readable name
"shortName": "Short name", // short name for launcher
"version": "0.01", // the version of this app
"author": "github_username",// username of app author (GitHub username or 'Espruino' for anonymity)
"description": "...", // long description (can contain markdown)
"icon": "icon.png", // icon in apps/
"screenshots" : [ { "url":"screenshot.png" } ], // optional screenshot for app
"type":"...", // optional(if app) -
// 'app' - an application
// 'clock' - a clock - required for clocks to automatically start
// 'widget' - a widget
// 'module' - this provides a module that can be used with 'require'.
// 'provides_modules' should be used if type:module is specified
// 'bootloader' - an app that at startup (app.boot.js) but doesn't have a launcher entry for 'app.js'
// 'settings' - apps that appear in Settings->Apps (with appname.settings.js) but that have no 'app.js'
// 'clkinfo' - Provides a 'myapp.clkinfo.js' file that can be used to display info in clocks - see modules/clock_info.js
// 'RAM' - code that runs and doesn't upload anything to storage
// 'launch' - replacement 'Launcher'
// 'textinput' - provides a 'textinput' library that allows text to be input on the Bangle
// 'scheduler' - provides 'sched' library and boot code for scheduling alarms/timers
// (currently only 'sched' app)
// 'notify' - provides 'notify' library for showing notifications
// 'locale' - provides 'locale' library for language-specific date/distance/etc
// (a version of 'locale' is included in the firmware)
// 'defaultconfig' - a set of apps that will can be installed and will wipe out all previously installed apps
"tags": "", // comma separated tag list for searching (don't include uppercase or spaces)
// common types are:
// 'clock' - it's a clock
// 'widget' - it is (or provides) a widget
// 'outdoors' - useful for outdoor activities
// 'tool' - a useful utility (timer, calculator, etc)
// 'game' - a game
// 'bluetooth' - uses Bluetooth LE
// 'system' - used by the system
// 'clkinfo' - provides or uses clock_info module for data on your clock face or clocks that support it (see apps/clock_info/README.md)
// 'health' - e.g. heart rate monitors or step counting
"supports": ["BANGLEJS2"], // List of device IDs supported, either BANGLEJS, BANGLEJS2, BANGLEJS3 or BANGLEJS3_COMPAT(faking the 176x176 LCD)
"dependencies" : { "notify":"type" } // optional, app 'types' we depend on (see "type" above)
"dependencies" : { "messages":"app" } // optional, depend on a specific app ID
// for instance this will use notify/notifyfs is they exist, or will pull in 'notify'
"dependencies" : { "messageicons":"module" } // optional, depend on a specific library to be used with 'require' - see provides_modules
"dependencies" : { "message":"widget" } // optional, depend on a specific type of widget - see provides_widgets
"provides_modules" : ["messageicons"] // optional, this app provides a module that can be used with 'require'
"provides_widgets" : ["battery"] // optional, this app provides a type of widget - 'alarm/battery/bluetooth/pedometer/message'
"provides_features" : ["welcome"] // optional, this app provides some feature, used to ensure two aren't installed at once. Currently just 'welcome'
"requires_firmware" : "2v20", // optional, specify a minimum firmware version your app needs to work properly
"default" : true, // set if an app is the default implementer of something (a widget/module/etc)
"readme": "README.md", // if supplied, a link to a markdown-style text file
// that contains more information about this app (usage, etc)
// A 'Read more...' link will be added under the app
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
// iframe, and it must post back an 'app' structure
// like this one with 'storage','name' and 'id' set up
// see below for more info
"customConnect": true, // if supplied, ensure we are connected to a device
// before the "custom.html" iframe is loaded. An
// onInit function in "custom.html" is then called
// with info on the currently connected device.
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
// iframe, and it may interact with the connected Bangle
// to retrieve information from it
// see below for more info
"allow_emulator":true, // if 'app.js' will run in the emulator, set to true to
// add an icon to allow your app to be tested
"storage": [ // list of files to add to storage
{"name":"appid.js", // filename to use in storage.
// If name=='RAM', the code is sent directly to Bangle.js and is not saved to a file
"url":"", // URL of file to load (currently relative to apps/)
"content":"...", // if supplied, this content is loaded directly
"evaluate":true, // if supplied, data isn't quoted into a String before upload
// (eg it's evaluated as JS)
"noOverwrite":true // if supplied, this file will not be overwritten if it
// already exists
"supports": ["BANGLEJS2"]// if supplied, this file will ONLY be uploaded to the device
// types named in the array. This allows different versions of
// the app to be uploaded for different platforms
},
]
"data": [ // list of files the app writes to
{"name":"appid.data.json", // filename used in storage
"storageFile":true // if supplied, file is treated as storageFile
"url":"", // if supplied URL of file to load (currently relative to apps/)
"content":"...", // if supplied, this content is loaded directly
"evaluate":true, // if supplied, data isn't quoted into a String before upload
// (eg it's evaluated as JS)
},
{"wildcard":"appid.data.*" // wildcard of filenames used in storage
}, // this is mutually exclusive with using "name"
],
"sortorder" : 0, // optional - choose where in the list this goes.
// this should only really be used to put system
// stuff at the top
}
- name、icon 和 description 在应用加载器中展示应用。
- tags 用于在库中对应用进行分组,多个条目之间用逗号分隔。已知的标签为
tool、system、clock、game、sound、gps、widget、launcher、bluetooth或为空。 - storage 用于识别应用文件及其处理方式
- data 用于在应用卸载时清理文件
metadata.json: custom 元素
可自定义的应用需要在 metadata.json 中定义一个 custom 元素,
该元素指定该应用文件夹中的一个 HTML 文件。
当定义 custom 时,'upload' 按钮会被替换为 customize
按钮,点击后会在 iframe 中打开指定的 HTML 页面。
在该 HTML 文件中,你需要负责处理按钮
点击事件,并调用 sendCustomizedApp,传入你对 metadata.json 中内容的自定义版本:
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<p><button id="upload" class="btn btn-primary">Upload</button></p>
<script src="../../lib/customize.js"></script>
<script>
document.getElementById("upload").addEventListener("click", function() {
sendCustomizedApp({
id : "myappid",
storage:[
{name:"myappid.app.js", url:"app.js", content:app_source_code},
{name:"myappid.img", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
]
});
});
</script>
</body>
</html>
然后这将被加载到手表中。参见 [apps/qrcode/grcode.html](the QR Code app) 以获取一个简洁的示例。
注意: 我们为 JS 文件指定了 url,即使它不必存在
且永远不会被加载。这样应用加载器可以根据扩展名判断它是否为 JavaScript
文件,如果是,则可以对其进行压缩和预标记化。
metadata.json: interface 元素
可以创建可回读数据的应用可以在 metadata.json 中定义一个 interface 元素,
该元素指定该应用文件夹中的一个 HTML 文件。
当定义了 interface 时,应用的描述中会添加一个 Download from App 按钮,
点击它会在 iframe 中打开指定的 HTML 页面。
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<script src="../../core/lib/interface.js"></script>
<div id="t">Loading...</div>
<script>
function onInit() {
Puck.eval("E.getTemperature()", temp=> {
document.getElementById("t").innerHTML = temp;
});
}
</script>
</body>
</html>
当页面准备就绪时,会调用一个名为 onInit 的函数,
在该函数中,你可以调用 Puck.write 和 Puck.eval 来从 Bangle.js 获取
所需的数据。
请参阅 [apps/gpsrec/interface.html](the GPS Recorder) 以查看完整示例。
在“设置”菜单中添加配置
应用程序(或小部件)可以在“设置”菜单下的“App/widget settings”中添加自己的设置。
为此,应用程序需要包含一个 settings.js 文件,其中包含一个用于
处理应用程序配置的函数。
当打开应用程序设置时,该函数会被调用,并带有一个
参数 back:一个用于返回设置菜单的回调函数。
通常,它会将信息保存在 myappid.json 中,其中 myappid 是
你的应用程序名称——因此你应该相应地修改示例。
示例 settings.js
// make sure to enclose the function in parentheses
(function(back) {
let settings = require('Storage').readJSON('myappid.json',1)||{};
if (typeof settings.monkeys !== "number") settings.monkeys = 12; // default value
function save(key, value) {
settings[key] = value;
require('Storage').write('myappid.json', settings);
}
const appMenu = {
'': {'title': 'App Settings'},
'< Back': back,
'Monkeys': {
value: settings.monkeys,
onchange: (m) => {save('monkeys', m)}
}
};
E.showMenu(appMenu)
})
在此示例中,应用程序需要在 metadata.json 中将 myappid.settings.js 添加到 storage。
它还应该将 myappid.json 添加到 data,以确保在应用程序卸载时进行清理。
{ "id": "myappid",
...
"storage": [
...
{"name":"myappid.settings.js","url":"settings.js"}
],
"data": [
{"name":"myappid.json"}
]
},
模块
你可以使用 require("modulename") 正常包含 Espruino 的模块。
为了在 Web IDE 中使用 Bangle 的模块,请 将模块上传到内部存储
或 更改 IDE 的搜索路径。
如果你想为你的
应用程序开发自己的模块,也可以这样做。只需将模块添加到 modules 文件夹
中,然后你就可以像平常一样在应用程序中使用它。
你无法使用 IDE 通过自己的模块来开发应用,
因此我们建议你先将模块写入 Bangle.js 上名为
modulename 的存储文件。然后你可以像往常一样在 Bangle.js
上从 IDE 开发你的应用。
编码提示
-
使用
g.setFont(.., size)来乘以字体大小,例如 ("6x8",3) : "18x24" -
使用
g.drawString(text,x,y,true)以背景色绘制,以覆盖现有文本 -
使用
g.clearRect()来清除屏幕的部分区域,而不是使用g.clear() -
使用
g.fillPoly()或g.drawImage()处理复杂的图形元素 -
使用
g.clear()可能会导致屏幕闪烁 -
使用
g.setLCDBrightness()可以在 LCD 长时间开启期间节省电量 -
链式调用图形方法,例如
g.setColor(0xFD20).setFontAlign(0,0).setfont("6x8",3)
其他说明
-
需要保存状态?使用
E.on('kill',...)事件将 JSON 保存到名为myappid.json的文件中,然后在启动时加载它。 -
“闹钟”应用定义了一个名为
alarm.js的文件,用于处理实际的闹钟窗口。 -
区域设置由
require("locale")处理。应用可以在存储中创建一个locale文件,该文件 是一个覆盖 Bangle.js 默认区域设置的模块。
图形区域
在 LCD 模式下,屏幕被划分为小部件区域和应用区域 direct(默认)。
| 区域 | 作为矩形或点 |
|---|---|
| 小部件 | (0,0,239,23) |
| 应用 | (0,24,239,239) |
| BTN1 | (230, 55) |
| BTN2 | (230, 140) |
| BTN3 | (230, 210) |
| BTN4 | (0,0,119, 239) |
| BTN5 | (120,0,239,239) |
-
使用
g.setFontAlign(0, 0, 3)配合g.drawString()在 BTN1-BTN3 上绘制旋转字符串。 -
对于 BTN4-5,触摸区域命名为
可用颜色
你可以使用 g.setColor(r,g,b) 或 g.setColor(16bitnumber) - 以下是一些常见的 16 位颜色:
| color-name | color-value |
|---|---|
| Black | 0x0000 |
| Navy | 0x000F |
| DarkGreen | 0x03E0 |
| DarkCyan | 0x03EF |
| Maroon | 0x7800 |
| Purple | 0x780F |
| Olive | 0x7BE0 |
| LightGray | 0xC618 |
| DarkGrey | 0x7BEF |
| Blue | 0x001F |
| Green | 0x07E0 |
| Cyan | 0x07FF |
| RED | 0xF800 |
| Magenta | 0xF81F |
| Yellow | 0xFFE0 |
| White | 0xFFFF |
| Orange | 0xFD20 |
| GreenYellow | 0xAFE5 |
| Pink | 0xF81F |
Fonts
Bangle.js 最近新增的功能是支持使用包含更多字符的额外字体。 例如 所有区域 或 扩展 字体。
安装后,这些应用会向 Graphics 添加一个新字体 Intl,只需使用 g.setFont("Intl") 即可调用。
还有一个 font 库 - 目前尚未实现,但有关计划实现的更多信息
可在 https://github.com/espruino/BangleApps/issues/3109 获取
目前,为了让您的应用支持国际字体,您可以检查 Graphics.prototype.setFontIntl 是否存在,
如果存在,您可以更改计划使用的字体:
myFont = "6x8:2";
if (Graphics.prototype.setFontIntl)
myFont = "Intl";
任何新的 Font 库都必须包含元数据 "icon": "app.png", "tags": "font", "type": "module", "provides_modules" : ["fonts"],
并且应提供一个 font 库,以及一个添加 Graphics.prototype.setFontIntl 的 boot.js。如果您计划
创建一个新的库,目前最好直接复制其中一个现有的库。
API Reference
'Testing' folder
testing 文件夹包含可能对您的应用有用的代码片段。
testing/colors.js- 作为名称值对的 16 位颜色testing/gpstrack.js- 将 GPS 轨迹存储在 Bangle.js 存储中并将其输出到控制台的代码
Credits
这些应用中使用的大部分图标来自 Icons8 - 我们拥有商业许可,但图标也可免费用于开源项目。