clexi
Version:
Node.js CLEXI is a lightweight client extension interface that enhances connected clients with functions of the underlying operating system using a duplex, realtime Websocket connection.
353 lines (336 loc) • 11.9 kB
HTML
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- web app settings -->
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0">
<!-- Title, icons and colors -->
<title>CLEXI Test</title>
<meta name="application-name" content="CLEXI Test"/>
<meta name="apple-mobile-web-app-title" content="CLEXI Test">
<meta name="description" content="CLEXI test-page">
<meta name="theme-color" content="#eee">
<!-- Icons
<link rel="icon" sizes="192x192" href="img/icon.png">
<link rel="apple-touch-icon" href="img/icon.png">
<meta property="og:image" content="img/icon.png"/>
-->
<!-- imports -->
<!--
<script type="text/javascript" src="js/jquery-3.1.1.min.js" charset="UTF-8"></script>
<link rel="stylesheet" href="css/main.css">
-->
<script type="text/javascript" src="lib/clexi-0.9.1.js" charset="UTF-8"></script>
<style>
html {
height: 100%;
}
body {
font-family: Sans-Serif;
display: flex;
flex-direction: column;
height: calc(100% - 16px);
}
button, input, label {
margin: 4px;
}
#log-out {
font-size: 15px;
color: #00bcd4;
background: #000;
padding: 4px 6px;
border-radius: 5px;
flex: 1 1 auto;
min-height: 200px;
overflow: auto;
}
#log-out > p {
margin: 8px 0;
}
</style>
</head>
<body>
<div>
<h3>Node.js Client Extension Interface - CLEXI.js - Test-Page</h3>
</div>
<div>
<h4>Connection</h4>
<label>Host:</label><input id="customHost"><br>
<label>Server ID:</label><input id="customServerId">
<div>
<button onclick="serverPing()">Ping CLEXI server</button>
<button onclick="connectToWebsocket()">Connect</button>
<button onclick="closeWebsocketConnection()">Close</button>
</div>
</div>
<h4>Xtensions</h4>
<div>
<label>Broadcast/HTTP-Events:</label>
<button onclick="websocketSendPing()">Broadcast Ping</button>
<button onclick="httpTestEvent()">Send Test HTTP-Event</button>
</div>
<div>
<label>Bluetooth BLE Beacon:</label>
<button onclick="websocketSendStartToBleBeaconScanner()">Start BLE Beacon Scanner</button>
<button onclick="websocketSendStopToBleBeaconScanner()">Stop BLE Beacon Scanner</button>
<button onclick="websocketSendStateRequestToBleBeaconScanner()">Get BLE Beacon Scanner State</button>
</div>
<div>
<label>Runtime Commands:</label>
<button onclick="websocketSendRuntimeCommand()">Call 'echo Hello World' in 3s</button>
</div>
<div>
<p><b>Xtension: GPIO Interface (Raspberry Pi):</b></p>
<div>
<button onclick="websocketGpioGetAll();">Get All Registered</button>
<button onclick="websocketGpioReleaseAll();">Release All</button>
</div>
<div>
<label>Button - Pin:</label><input id="gpio-button-pin" type="number">
<button onclick="websocketRegisterGpioButton(document.getElementById('gpio-button-pin').value);">Register</button>
<button onclick="websocketReleaseGpioButton(document.getElementById('gpio-button-pin').value);">Release</button>
</div>
<div>
<label>LED - Pin:</label><input id="gpio-led-pin" type="number">
<button onclick="websocketRegisterGpioLed(document.getElementById('gpio-led-pin').value);">Register</button>
<button onclick="websocketReleaseGpioLed(document.getElementById('gpio-led-pin').value);">Release</button>
<button onclick="websocketSetGpioLed(document.getElementById('gpio-led-pin').value, 1);">1</button>
<button onclick="websocketSetGpioLed(document.getElementById('gpio-led-pin').value, 0);">0</button>
</div>
<div>
<label>ReSpeaker Mic HAT LEDs:</label>
<button onclick="websocketRegisterGpioRespeakerMicHat();">Register</button>
<button onclick="websocketReleaseGpioRespeakerMicHat();">Release</button>
<button onclick="websocketGetGpioRespeakerMicHat();">Get</button>
<button onclick="websocketSetGpioRespeakerMicHat(1, 0, 150, 0);">(1) Green</button>
<button onclick="websocketSetGpioRespeakerMicHat(1, 100, 100, 0);">(1) Yellow</button>
<button onclick="websocketSetGpioRespeakerMicHat(1, 150, 0, 0);">(1) Red</button>
<button onclick="websocketSetAllRandomGpioRespeakerMicHat();">All Random</button>
<button onclick="websocketSetAllOffGpioRespeakerMicHat();">All Off</button>
</div>
</div>
<h4>Client log: <button onclick="document.getElementById('log-out').innerHTML = '';">clear</button></h4>
<div id="log-out"><p>Welcome - Please connect.</p></div>
<div style="height: 4px; width: 100%; flex: 0 0 4px;"></div>
<script>
var logOut = document.getElementById('log-out');
var customHost = document.getElementById('customHost');
var customServerId = document.getElementById('customServerId');
var cachedHost = ('sessionStorage' in window)? sessionStorage.getItem('hostURL') : undefined;
if (cachedHost){
customHost.value = cachedHost;
}else{
customHost.value = location.href.replace(/\/index\.html.*/, "").replace(/\/$/, "").replace(/^http(s|):/, "ws$1:");
}
function log(msg){
console.log(msg);
var msgEle = document.createElement("p");
msgEle.textContent = msg;
logOut.appendChild(msgEle);
logOut.scrollTop = logOut.scrollHeight;
}
function err(e){
console.error(e);
var msgEle = document.createElement("p");
msgEle.style.color = "#e91e4d";
msgEle.textContent = (e && e.msg)? e.msg : e;
logOut.appendChild(msgEle);
logOut.scrollTop = logOut.scrollHeight;
}
//CLEXI Demo
ClexiJS.clientBaseId = "TEST-CL-";
ClexiJS.onLog = log;
ClexiJS.onDebug = log;
ClexiJS.onError = err;
ClexiJS.serverId = "clexi-123";
customServerId.value = ClexiJS.serverId;
function connectToWebsocket(){
ClexiJS.serverId = customServerId.value;
var hostURL = customHost.value || cachedHost || ""; //auto-choose origin
if ('sessionStorage' in window) sessionStorage.setItem('hostURL', hostURL);
//NOTE: something to try as well: ClexiJS.pingAndConnect(host, onPingOrIdError, onOpen, onClose, onError, onConnecting);
ClexiJS.connect(hostURL, function(e){
//log("CLEXI - ready.");
}, function(e){
//log("CLEXI - lost connection.");
removeSubscriptions();
}, function(err){
//log("CLEXI - something went wrong.");
removeSubscriptions();
}, function(){
//log("CLEXI - connecting.");
}, function(welcomeInfo){
//log("CLEXI - welcome event.");
//subscribe to extension events:
subscribeAllAvailable(welcomeInfo);
});
//log("Connecting to CLEXI...");
}
function closeWebsocketConnection(){
ClexiJS.close();
}
function serverPing(){
ClexiJS.serverId = customServerId.value;
var hostURL = customHost.value || cachedHost || ""; //auto-choose origin
if (!hostURL) hostURL = location.origin;
else hostURL = hostURL.replace(/^wss/, 'https').replace(/^ws/, 'http');
ClexiJS.httpRequest("GET", hostURL + "/ping", function(data){
//Success
log("HTTP GET result to 'ping' endpoint: " + data);
}, function(){
//Error
err({msg:"CLEXI connection failed! Server not reached."});
});
}
function httpTestEvent(){
var hostURL = customHost.value || cachedHost || ""; //auto-choose origin
if (!hostURL) hostURL = location.origin;
else hostURL = hostURL.replace(/^wss/, 'https').replace(/^ws/, 'http');
//Test event via HTTP GET. HTTP POST is possible too, but not yet with this client method (v0.8.1, build your own POST).
var eventName = "testEvent";
var eventData = {
"num": 42
};
ClexiJS.sendHttpEvent(hostURL, ClexiJS.serverId, eventName, eventData, function(data){}, function(){
//Error
err({msg:"CLEXI connection failed! Server not reached or not authorized."});
});
}
//Broadcaster
function websocketSendPing(){
ClexiJS.send('clexi-broadcaster', {
text: "ping"
});
}
//BLE Beacon
function websocketSendStartToBleBeaconScanner(){
ClexiJS.send('ble-beacon-scanner', {
ctrl: "start"
});
}
function websocketSendStopToBleBeaconScanner(){
ClexiJS.send('ble-beacon-scanner', {
ctrl: "stop"
});
}
function websocketSendStateRequestToBleBeaconScanner(){
ClexiJS.send('ble-beacon-scanner', {
ctrl: "state"
});
}
//Runtime commands
var rtcCmdId = 1;
function websocketSendRuntimeCommand(){
ClexiJS.send('runtime-commands', {
id: "CMD-" + ++rtcCmdId,
cmd: "callCustom",
args: {
delay: 3000,
file: "echo",
TEXT: "Hello World"
}
});
}
//GPIO-Interface
function websocketSendGpioInterfaceRequest(action, type, config){
ClexiJS.send('gpio-interface', {
action: action,
type: type,
config: config
});
}
function websocketGpioGetAll(){
websocketSendGpioInterfaceRequest("get", "all", {});
}
function websocketGpioReleaseAll(){
websocketSendGpioInterfaceRequest("release", "all", {});
}
function websocketRegisterGpioButton(pin){
websocketSendGpioInterfaceRequest("register", "button", {pin: pin});
}
function websocketReleaseGpioButton(pin){
websocketSendGpioInterfaceRequest("release", "button", {pin: pin});
}
function websocketRegisterGpioLed(pin){
websocketSendGpioInterfaceRequest("register", "led", {pin: pin});
}
function websocketReleaseGpioLed(pin){
websocketSendGpioInterfaceRequest("release", "led", {pin: pin});
}
function websocketSetGpioLed(pin, value){
websocketSendGpioInterfaceRequest("set", "led", {pin: pin, value: value});
}
//GPIO-Interface - Custom Item - ReSpeaker Mic HAT (SPI interface)
function websocketRegisterGpioRespeakerMicHat(){
websocketSendGpioInterfaceRequest("register", "item", {
file: "rpi-respeaker-mic-hat-leds",
options: {numOfLeds: 3}
});
}
function websocketReleaseGpioRespeakerMicHat(){
websocketSendGpioInterfaceRequest("release", "item", {
file: "rpi-respeaker-mic-hat-leds"
});
}
function websocketGetGpioRespeakerMicHat(){
websocketSendGpioInterfaceRequest("get", "item", {
file: "rpi-respeaker-mic-hat-leds"
});
}
function websocketSetGpioRespeakerMicHat(ledIndex, red, green, blue){
websocketSendGpioInterfaceRequest("set", "item", {
file: "rpi-respeaker-mic-hat-leds",
data: {ledIndex: ledIndex, red: red, green: green, blue: blue}
});
}
function websocketSetAllRandomGpioRespeakerMicHat(){
websocketSetGpioRespeakerMicHat(1, getRandomLedValue(), getRandomLedValue(), getRandomLedValue());
websocketSetGpioRespeakerMicHat(2, getRandomLedValue(), getRandomLedValue(), getRandomLedValue());
websocketSetGpioRespeakerMicHat(3, getRandomLedValue(), getRandomLedValue(), getRandomLedValue());
}
function websocketSetAllOffGpioRespeakerMicHat(){
websocketSetGpioRespeakerMicHat(1, 0, 0, 0);
websocketSetGpioRespeakerMicHat(2, 0, 0, 0);
websocketSetGpioRespeakerMicHat(3, 0, 0, 0);
}
function getRandomLedValue(){
return Math.round(Math.random() * 255 * 0.2);
}
//Common subscription method
function subscribeToXtension(xName){
ClexiJS.subscribeTo(xName, function(e){
log(xName + ' event: ' + JSON.stringify(e));
}, function(e){
log(xName + ' response: ' + JSON.stringify(e));
}, function(e){
log(xName + ' error: ' + JSON.stringify(e));
});
}
function subscribeAllAvailable(welcomeInfo){
//subscribe all active xtensions
if (welcomeInfo && welcomeInfo.xtensions){
var xtensionNames = Object.keys(welcomeInfo.xtensions);
xtensionNames.forEach(function(xn){
//check if xtension is active
if (welcomeInfo.xtensions[xn].active){
subscribeToXtension(xn);
subscriptions.push(xn);
}
});
}
log("Subscriptions: " + JSON.stringify(subscriptions));
}
function removeSubscriptions(){
//unsubscribe all active xtensions
subscriptions.forEach(function(xn){
ClexiJS.removeSubscription(xn);
});
if (subscriptions.length){
log("Unsubscribed all");
}
subscriptions = [];
}
var subscriptions = [];
</script>
</body>
</html>