<!DOCTYPE html>
<html lang="en">
<!--
https://www.thebubbleworks.com/TheBubbleWorks_WebBluetooth_PythonBlueZero_LightSwitch
http://plnkr.co/edit/eJeCIn?p=preview
-->
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0" />
<title>Python BlueZero - Light Example</title>
<script data-require="jquery@*" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<!-- FontAwesome is a collection of icons, this uses the lightbulb, bluetooth and the switch -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css" />
<!-- App specifc styles -->
<link rel="stylesheet" href="app.css" />
<!-- WORK IN PROGRESS - AGENT NOT YET PUBLISHED -->
<!-- If the browser aand platform doesn't support WebBluetooth at all it's possible to fall back to using
a 'sidekick' that runs as a local service on the client machine by injecting this optional shim.
The BubbleWorks cross platform & cross browser Web Bluetooth shim, requires client/native service to be running on local machine.
See here: https://www.thebubbleworks.com/WebBluetoothSidekick/latest/agent/
-->
<!--
<script src="https://www.thebubbleworks.com/WebBluetoothSidekick/latest/browser/lib/bluetooth.helpers.js"></script>
<script src="https://www.thebubbleworks.com/WebBluetoothSidekick/latest/browser/lib/adapter.bubbleworks.js"></script>
-->
</head>
<body>
<div class="fixed-top-left">
<div>
<i class="fa fa-bluetooth fa-5x blue"></i>
</div>
<div>
<button id="connectButton">
<i class="fa fa-toggle fa-toggle-off fa-5x blue" id="bluetoothSwitch"></i>
</button>
</div>
</div>
<div class="fixed-top-right">
<div>
<i class="fa fa-lightbulb-o lightBulbOff" id="lightBulb"></i>
</div>
<div>
<button id="offButton">
<i class="fa fa-toggle fa-toggle-off fa-5x red" id="lightSwitch"></i>
</button>
</div>
</div>
<div id="footer">
<h2>LightBulb Demo for Python <a href="https://github.com/ukBaz/python-bluezero">BlueZero</a></h2>
<textarea class="console" id="consoleTextArea" readonly=""></textarea>
</div>
<script src="app.js"></script>
</body>
</html>
//-------------------------------------------------------------------------
// Logging
function error(text) {
log("ERROR:" + text);
}
function info(text) {
log("INFO: " + text);
}
function log(line) {
console.log(line);
textarea = document.getElementById('consoleTextArea')
if (textarea) {
previous_text = textarea.innerHTML;
textarea.innerHTML = previous_text + line + "\n";
textarea.scrollTop = textarea.scrollHeight;
}
}
window.onerror = function(msg, url, lineNumber, columnNumber, err) {
error(msg + ' Script: ' + url + ' Line: ' + lineNumber);
return false;
}
//-------------------------------------------------------------------------
// App starts when DOM is ready
$(document).ready(function() {
info("App started");
// UI Input handlers
$('#connectButton').click(connectPressed);
$('#lightSwitch').click(switchPressed);
updateDisplay();
});
//-------------------------------------------------------------------------
// Constants
// Bluetooth
// These 128-Bit ID's correspond to the python-bluezero light switch example
var SERVICE_UUID = '12341000-1234-1234-1234-123456789abc';
var CHAR_RX_UUID = '12341002-1234-1234-1234-123456789abc';
var CHAR_TX_UUID = '12341002-1234-1234-1234-123456789abc';
var LIGHT_OFF = [0x00];
var LIGHT_ON = [0x01];
//-------------------------------------------------------------------------
// App State
var isLightOn = false;
var connected = false;
//-------------------------------------------------------------------------
// Bluetooth state and connection setup
var bleDevice = null;
var gattServer = null;
var gattService = null;
var writeCharacteristic = null;
var readCharacteristic = null;
function setupBluetooth() {
if (navigator.bluetooth === undefined) {
error('Web Bluetooth support not found, please see: https://goo.gl/5p4zNM');
return;
}
if (gattServer !== null && gattServer.connected) {
info('(not yet) Disconnecting...');
// TODO: inspect platform support, and/or
// TODO: listen for an actual BLE event#
//gattServer.disconnect();
//updateBluetoothState(false);
} else {
info('Connecting...');
if (readCharacteristic === null) {
navigator.bluetooth.requestDevice({
filters: [{
services: [SERVICE_UUID]
}]
})
.then(function(device) {
info('DeviceName=' + device.name);
info('Connecting to GATT Server...');
bleDevice = device;
if (device.gatt)
return device.gatt.connect(); // Connect to GATT Server on device
else
return device.connectGATT(); // deprecated but a fallback
}).then(function(server) {
info('Found GATT server');
gattServer = server;
return gattServer.getPrimaryService(SERVICE_UUID); // Get service
}).then(function(service) {
info('Found service');
gattService = service;
return gattService.getCharacteristic(CHAR_TX_UUID); // Get write characteristic
}).then(function(characteristic) {
info('Found write characteristic');
writeCharacteristic = characteristic;
return gattService.getCharacteristic(CHAR_RX_UUID); // Get read characteristic
}).then(function(characteristic) {
connected = true;
info('Found read characteristic');
readCharacteristic = characteristic;
updateBluetoothState(true);
// Listen to device notifications
return readCharacteristic.startNotifications().then(function() {
readCharacteristic.addEventListener('characteristicvaluechanged', function(event) {
info('characteristicvaluechanged = ' + event.target.value + ' [' + event.target.value.byteLength + ']');
if (event.target.value.byteLength > 0) {
var data = new Uint8Array(event.target.value);
onDataReceived(data);
}
});
});
}).catch(handleError);
}
}
}
function handleError(err) {
error(err);
updateBluetoothState(false);
}
//-------------------------------------------------------------------------
// Bluetooth sending and receiving
function send(data) {
info("Sending: " + data);
try {
if (writeCharacteristic)
writeCharacteristic.writeValue(new Uint8Array(data));
else
error("writeCharacteristic is not set");
} catch (err) {
error("Couldn't send, not connected? error was: " + err);
}
}
function onDataReceived(data) {
info("Recv data: " + data);
if (data.length === 0)
return;
updateLightState(data[0] == 0x01);
}
//-------------------------------------------------------------------------
// UI Inputs
function connectPressed() {
setupBluetooth();
}
function switchPressed() {
isLightOn = !isLightOn;
updateLightState(isLightOn);
}
//-------------------------------------------------------------------------
// UI Display
function updateDisplay() {
updateBluetoothDisplay();
updateLightDisplay();
}
function updateBluetoothDisplay() {
if (connected)
$('#bluetoothSwitch').removeClass('fa-toggle-off').addClass('fa-toggle-on');
else
$('#bluetoothSwitch').removeClass('fa-toggle-on').addClass('fa-toggle-off');
}
function updateLightDisplay() {
if (isLightOn) {
$('#lightBulb').removeClass('lightBulbOff').addClass('lightBulbOn');
$('#lightSwitch').removeClass('fa-toggle-off').addClass('fa-toggle-on');
} else {
$('#lightBulb').removeClass('lightBulbOn').addClass('lightBulbOff');
$('#lightSwitch').removeClass('fa-toggle-on').addClass('fa-toggle-off');
}
}
//-------------------------------------------------------------------------
// State handling
function updateBluetoothState(newConnectedState) {
connected=newConnectedState;
updateBluetoothDisplay();
}
function updateLightState(newIsLightOnState) {
if (newIsLightOnState) {
isLightOn = true;
send(LIGHT_ON);
} else {
isLightOn = false;
send(LIGHT_OFF);
}
updateLightDisplay();
}
body {
background-color: lightgrey;
font-family: "Trebuchet MS", Helvetica, sans-serif;
}
.fa {
text-shadow: 3px 3px 6px #888888;
}
.fa-bluetooth {
color: blue;
font-size: 64px;
}
.fa-lightbulb-o {
color: lightgrey;
font-size: 196px;
}
.fa-toggle {
font-size: 48px;
color: grey;
}
.lightBulbOff {
color: grey;
}
.lightBulbOn {
color: yellow;
}
.inner {
width: 50%;
margin: 0 auto;
}
.container {
position: relative;
width: 100%;
}
.inlineLeft,
.inlineRight {
position: absolute;
display: inline;
}
.inlineRight {
right: 0;
}
button {
border: none;
padding: 20;
background: none;
}
.console {
width: 80%;
height: 240px;
border: 5px solid #cccccc;
padding: 5px;
font-family: "Lucida Console", Monaco, monospace;
background-color: lightgrey;
color: black;
}
.fixed-top-right {
position: fixed;
top: 1em;
right: 1em;
}
.fixed-top-left {
position: fixed;
top: 1em;
left: 1em;
}
#footer {
position: fixed;
bottom: 0;
width: 100%;
}