UNPKG

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.

393 lines (375 loc) 13.5 kB
<!DOCTYPE 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.10.0.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> <label>Proto Voice HAT LEDs (SPI WS281X):</label> <button onclick="websocketRegisterGpioProtoVoiceHat();">Register</button> <button onclick="websocketReleaseGpioProtoVoiceHat();">Release</button> <button onclick="websocketGetGpioProtoVoiceHat();">Get</button> <button onclick="websocketSetGpioProtoVoiceHat(1, 0, 150, 0);">(1) Green</button> <button onclick="websocketSetGpioProtoVoiceHat(1, 100, 100, 0);">(1) Yellow</button> <button onclick="websocketSetGpioProtoVoiceHat(1, 150, 0, 0);">(1) Red</button> <button onclick="websocketSetGpioProtoVoiceHat(1, 0, 0, 0);">(1) 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."); if (err?.name == "AlreadyConnected"){ //ignore }else{ 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'); var pingUrl = hostURL + "/ping"; log("Sending ping request to: " + pingUrl + " ..."); ClexiJS.httpRequest("GET", pingUrl, 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); } //GPIO-Interface - Custom Item - Custom Proto Voice HAT (WS281X SPI interface) function websocketRegisterGpioProtoVoiceHat(){ websocketSendGpioInterfaceRequest("register", "item", { file: "rpi-spi-rgb-leds", options: {numOfLeds: 1, ledType: "ws281x"} }); } function websocketReleaseGpioProtoVoiceHat(){ websocketSendGpioInterfaceRequest("release", "item", { file: "rpi-spi-rgb-leds" }); } function websocketGetGpioProtoVoiceHat(){ websocketSendGpioInterfaceRequest("get", "item", { file: "rpi-spi-rgb-leds" }); } function websocketSetGpioProtoVoiceHat(ledIndex, red, green, blue){ websocketSendGpioInterfaceRequest("set", "item", { file: "rpi-spi-rgb-leds", data: {ledIndex: ledIndex, red: red, green: green, blue: blue} }); } 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>