Add Espruino terminal - to allow communications with Espruino devices via terminal

pull/2055/head^2
Gordon Williams 2022-07-19 12:19:40 +01:00
parent da31affbea
commit 1369047259
12 changed files with 259 additions and 4 deletions

View File

@ -324,7 +324,7 @@ and which gives information about the app for the Launcher.
```
* name, icon and description present the app in the app loader.
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty.
* storage is used to identify the app files and how to handle them
* data is used to clean up files when the app is uninstalled

View File

@ -5,7 +5,7 @@
"version": "0.01",
"description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!",
"icon": "app.png",
"tags": "",
"tags": "tool,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"custom": "custom.html",

View File

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

View File

@ -0,0 +1,22 @@
# Espruino Terminal
Send commands to other Espruino devices via the Bluetooth UART interface and
see the result on a terminal.
## Customising
Once installed and you're connected to the Bangle you can click the button next to the app in the app loader
to change the commands (they will be read from the device).
When done, click `Save to Bangle.js` and your changes will be saved to the same device.
## Usage
* Load the app and after a few seconds you'll see a menu with Espruino devices
in the vicinity.
* Tap on the device you want to connect to
* A terminal will pop up showing `Connecting...` and then `Connected`
* Now tap on the right (or press the button) to bring up a menu with options for commands, or the option to disconnect.
You can also choose `Custom` in which case a keyboard (using the currently installed text input method) will
be displayed and you can enter the command you would like to send.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcCpMkyQC/AVW//4AK/oR/COD8LCP4R/CK8DCKNsCKFt2BHPhu2CJ8BCKAjQI4OQNaIUB23bsCPMCJzp/CP4Rf/4AKCKwC/AVIA=="))

101
apps/espruinoterm/app.js Normal file
View File

@ -0,0 +1,101 @@
var uart; // require("ble_uart")
var device; // BluetoothDevice
var customCommand = "";
// Set up terminal
Bangle.loadWidgets();
var R = Bangle.appRect;
var termg = Graphics.createArrayBuffer(R.w, R.h, 1, {msb:true});
var termVisible = false;
termg.setFont("6x8");
term = require("VT100").connect(termg, {
charWidth : 6,
charHeight : 8
});
term.print = str => {
for (var i of str) term.char(i);
if (termVisible) g.reset().drawImage(termg,R.x,R.y).setFont("6x8").setFontAlign(0,-1,1).drawString("MORE",R.w-1,(R.y+R.y2)/2);
};
function showConnectMenu() {
termVisible = false;
var m = { "" : {title:"Devices"} };
E.showMessage("Scanning...");
NRF.findDevices(devices => {
devices.forEach(dev=>{
m[dev.name||dev.id.substr(0,17)] = ()=>{
connectTo(dev);
};
});
m["< Back"] = () => showConnectMenu();
E.showMenu(m);
},{filters:[
{ namePrefix: 'Puck.js' },
{ namePrefix: 'Pixl.js' },
{ namePrefix: 'MDBT42Q' },
{ namePrefix: 'Bangle.js' },
{ namePrefix: 'Espruino' },
{ services: [ "6e400001-b5a3-f393-e0a9-e50e24dcca9e" ] }
],active:true,timeout:4000});
}
function showOptionsMenu() {
if (!uart) showConnectMenu();
termVisible = false;
var menu = {"":{title:/*LANG*/"Options"},
"< Back" : () => showTerminal(),
};
var json = require("Storage").readJSON("espruinoterm.json",1);
if (Array.isArray(json)) {
json.forEach(j => { menu[j.title] = () => sendCommand(j.cmd); });
} else {
Object.assign(menu,{
"Version" : () => sendCommand("process.env.VERSION"),
"Battery" : () => sendCommand("E.getBattery()"),
"Flash LED" : () => sendCommand("LED.set();setTimeout(()=>LED.reset(),1000);")
});
}
menu[/*LANG*/"Custom"] = () => { require("textinput").input({text:customCommand}).then(result => {
customCommand = result;
sendCommand(customCommand);
})};
menu[/*LANG*/"Disconnect"] = () => { showTerminal(); term.print("\r\nDisconnecting...\r\n"); uart.disconnect(); }
E.showMenu(menu);
}
function showTerminal() {
E.showMenu();
Bangle.setUI({
mode : "custom",
btn : n=> { showOptionsMenu(); },
touch : (n,e) => { if (n==2) showOptionsMenu(); }
});
termVisible = true;
term.print(""); // redraw terminal
}
function sendCommand(cmd) {
showTerminal();
uart.write(cmd+"\n");
}
function connectTo(dev) {
device = dev;
showTerminal();
term.print(`\r\nConnect to ${dev.name||dev.id.substr(0,17)}...\r\n`);
device.on('gattserverdisconnected', function(reason) {
term.print(`\r\nDISCONNECTED (${reason})\r\n`);
uart = undefined;
device = undefined;
setTimeout(showConnectMenu, 1000);
});
require("ble_uart").connect(device).then(function(u) {
uart = u;
term.print("Connected...\r\n");
uart.on('data', function(d) { term.print(d); });
});
}
// now start
Bangle.drawWidgets();
showConnectMenu();

View File

@ -0,0 +1,5 @@
[
{"title":"Version", "cmd":"process.env.VERSION"},
{"title":"Battery", "cmd":"E.getBattery()"},
{"title":"Flash LED", "cmd":"LED.set();setTimeout(()=>LED.reset(),1000);"}
]

BIN
apps/espruinoterm/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

View File

@ -0,0 +1,104 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-exp.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<style>
.badgeerror[data-badge]::after { background-color: red; }
</style>
</head>
<body>
<p>Enter the menu items you'd like to see appear in the app below. When finished, click `Save to Bangle.js` to save the JavaScript back.</p>
<div style="overflow-x:scroll">
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Command</th>
<th></th>
</tr>
</thead>
<tbody id="tbody">
</tbody>
</table>
</div>
<p><button id="save" class="btn btn-primary">Save to Bangle.js</button></p>
<script src="../../core/lib/interface.js"></script>
<script>
var options;
setDefaults();
function setDefaults() {
options = [
{title:"Version", cmd:"process.env.VERSION"},
{title:"Battery", cmd:"E.getBattery()"},
{title:"Flash LED", cmd:"LED.set();setTimeout(()=>LED.reset(),1000);"}
];
}
function refresh() {
var tbody = document.getElementById("tbody");
tbody.innerHTML = "";
options.forEach((option,idx)=>{
var tr = document.createElement("tr");
tr.innerHTML = `
<td><input type="text" name="title"></td>
<td><input type="text" name="command"></td>
<td><button class="btn btn-action"><i class="icon icon-delete"></i></button></td>
`;
var titleInput = tr.children[0].firstChild;
titleInput.value = option.title;
titleInput.addEventListener("change", function(e) {
option.title = titleInput.value;
});
var cmdInput = tr.children[1].firstChild;
cmdInput.value = option.cmd;
cmdInput.addEventListener("change", function(e) {
option.cmd = cmdInput.value;
});
tr.children[2].firstChild.addEventListener("click", function() {
options.splice(idx,1);
refresh();
});
tbody.appendChild(tr);
});
var tr = document.createElement("tr");
tr.innerHTML = `
<td><button class="btn">Reset to Defaults</button></td>
<td></td>
<td><button class="btn btn-action"><i class="icon icon-plus"></i></button></td>
`;
tr.children[0].firstChild.addEventListener("click", function() {
setDefaults();
refresh();
});
tr.children[2].firstChild.addEventListener("click", function() {
options.push({"title":"",cmd:""});
refresh();
});
tbody.appendChild(tr);
}
function onInit() {
Util.showModal("Loading...");
Util.readStorage("espruinoterm.json", function(j) {
Util.hideModal();
try {
options = JSON.parse(j);
} catch (e) {}
if (!Array.isArray(options)) setDefaults();
refresh();
});
}
refresh();
document.getElementById("save").addEventListener("click", function() {
Util.showModal("Saving...");
Util.writeStorage("espruinoterm.json", JSON.stringify(options), function() {
Util.hideModal();
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,20 @@
{
"id": "espruinoterm",
"name": "Espruino Terminal",
"shortName": "Espruino Term",
"version": "0.01",
"description": "Send commands to other Espruino devices via the Bluetooth UART interface, and see the result on a VT100 terminal. Customisable commands!",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"tags": "tool,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"interface": "interface.html",
"dependencies": {"textinput":"type"},
"storage": [
{"name":"espruinoterm.app.js","url":"app.js"},
{"name":"espruinoterm.img","url":"app-icon.js","evaluate":true}
],"data": [
{"name":"espruinoterm.json","url":"app.json"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -6,10 +6,11 @@
"type":"textinput",
"tags": "keyboard",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}],
"screenshots": [{"url":"screenshot.png"}],
"readme": "README.md",
"storage": [
{"name":"textinput","url":"lib.js"},
{"name":"kbtouch.settings.js","url":"settings.js"}
]
],
"sortorder":-1
}