/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* global BATTERY, BUTTON */ var app = { stop: false, log: {}, activeServices: [], list: {}, manufactureDecoder: new MANUFACTUREDECODER(), // Application Constructor initialize: function() { this.bindEvents(); }, arrayBufferToIntArray: function(buffer) { var result; if (buffer) { var typedArray = new Uint8Array(buffer); result = []; for (var i = 0; i < typedArray.length; i++) { result[i] = typedArray[i]; } } return result; }, parseAdvertisingData: function(bytes) { var length, type, data, i = 0, advertisementData = {}; while (length !== 0) { length = bytes[i] & 0xFF; i++; type = bytes[i] & 0xFF; i++; data = bytes.slice(i, i + length - 1); // Length includes type byte, but not length byte i += length - 2; // Move to end of data i++; advertisementData[type] = data; } return advertisementData; }, handle255: function(buffer) { 'use strict'; var company; var cid; var manID; var bin = buffer; var decoded = {}; console.log('Block255', bin); var manSpec = bin.map(function(i) { return i.toString(16) + ' '; }); console.log('manSpec: ', manSpec); manID = app.manufactureDecoder.getManID(bin); console.log('ManID:', manID); cid = '0x' + manID; company = bt_company_ids.find(cid); switch (manID) { case '004c': decoded = app.manufactureDecoder.decodeIbeacon(bin); decoded.company = company; return decoded; break; case '1235': decoded = app.manufactureDecoder.decodeSiliconLabsSensorPuck(bin); decoded.company = company; return decoded; break; case '0060': decoded = app.manufactureDecoder.decodeSansible(bin); decoded.company = company; return decoded; break; default: console.log('Unknown manID: ', manID); } return {company:company}; }, makeHexBuffer : function(buffer) { 'use strict'; var hexBuffer = buffer.map(function(i) { return ('00' + i.toString(16)).slice(-2) + ','; }); return hexBuffer; }, makeChars : function(buffer) { 'use strict'; var output = buffer.map(function(i) { return String.fromCharCode(i); }); return output; }, doScan: function(mode) { 'use strict'; $('#ripple').show(); if (mode !== 2) { $('#tbody').empty(); } var otherData = null; var msgText = ''; ble.startScan([], function(device) { var parsed; var hexBuffer; var advertBuffer; var newTR; var newId; var _device = device; otherData = null; msgText = ''; console.log(JSON.stringify(device)); newId = device.id.replace(/:/g, ''); console.log(newId); this.list[newId] = device.id; newTR = $(''); newTR.append($('').text(device.id)); if (device.hasOwnProperty('advertising')) { advertBuffer = app.arrayBufferToIntArray(device.advertising); hexBuffer = app.makeHexBuffer(advertBuffer); parsed = app.parseAdvertisingData(advertBuffer); //Console.log(parsed); if (parsed.hasOwnProperty('9')) { var name = app.makeChars(parsed['9']); _device.name = name.join(''); console.log('Name: ', name.join('')); } if (parsed.hasOwnProperty('255')) { otherData = app.handle255(parsed['255']); console.log(otherData); _device.otherData = otherData; } _device.advertBuffer = advertBuffer; _device.hexBuffer = hexBuffer; _device.parsed = parsed; } if (typeof otherData !== 'undefined' && otherData !== null) { if (otherData.hasOwnProperty('msg')) { msgText = ' - ' + otherData.msg; } } if (device.hasOwnProperty('name')) { newTR.append($('').text(device.name + msgText)); } else { newTR.append($('').text('*** Unknown' + msgText)); } newTR.append($('').text(device.rssi)); if ($('tr#' + newId).length > 0) { $('tr#' + newId).replaceWith(newTR); } else { $('#tbody').append(newTR); } //$('#output').append(JSON.stringify(device) + '
'); app.log[newId] = _device; console.log(JSON.stringify(_device)); }.bind(this), function(e) { 'use strict'; console.error(e); }); var _t = [5000,60000,200][mode]; setTimeout(ble.stopScan, _t, function() { console.log('Scan complete'); if (mode === 1) { app.saveLog(); $('#ripple').hide(); } if (mode === 2) { if (!app.stop) { setTimeout(function() { app.doScan(2); }.bind(this), 200); } else { app.saveLog(); $('#ripple').hide(); } } }, function() { console.log('stopScan failed'); $('#ripple').hide(); }); }, writeFile: function(fileEntry, dataObj) { // Create a FileWriter object for our FileEntry (log.txt). fileEntry.createWriter(function(fileWriter) { fileWriter.onwriteend = function() { console.log('Successful file write...'); // ReadFile(fileEntry); }; fileWriter.onerror = function(e) { console.error('Failed file write: ' + e.toString()); }; // If data object is not passed in, // create a new Blob instead. if (!dataObj) { dataObj = new Blob(['some file data'], { type: 'text/plain' }); } fileWriter.write(dataObj); }); }, saveLog: function() { 'use strict'; var dt = new Date().toISOString().replace(/:|-/g,'').replace(/(\.\w+)/g,''); var payload = JSON.stringify(app.log); var filename = 'sensortoy-' + dt + '.json'; // Var dataObj = new Blob(payload, { type: 'text/plain' }); window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) { console.log('file system open: ' + fs.name); fs.root.getFile(filename, { create: true, exclusive: false }, function(fileEntry) { console.log('fileEntry is file?' + fileEntry.isFile.toString()); // FileEntry.name == 'someFile.txt' // fileEntry.fullPath == '/someFile.txt' console.log('Path: ', fileEntry.fullPath); app.writeFile(fileEntry, payload); app.log = []; }, app.onError); }, app.onError); }, forceStop: function() { 'use strict'; app.stop = true; $('#scan').show(); $('#stop').hide(); }, // Bind Event Listeners // // Bind any events that are required on startup. Common events are: // 'load', 'deviceready', 'offline', and 'online'. bindEvents: function() { var self = this; document.addEventListener('deviceready', this.onDeviceReady, false); $('#scan').on('click', function() { 'use strict'; this.stop = false; this.doScan(2); $('#scan').hide(); $('#stop').show(); }.bind(this)); $('#stop').on('click', function() { 'use strict'; app.forceStop(); }.bind(this)); $('#longScan').on('click', function() { 'use strict'; this.doScan(1); }.bind(this)); $('#tbody').on('click', 'tr', function() { 'use strict'; var tID = $(this).context.id; var id = self.list[tID]; console.log(tID, id); app.forceStop(); self.connect(id); }); }, // Deviceready Event Handler // // The scope of 'this' is the event. In order to call the 'receivedEvent' // function, we must explicitly call 'app.receivedEvent(...);' onDeviceReady: function() { }, serviceDiscovery: function(services) { 'use strict'; console.log(services); }, sensorMpu9250GyroConvert: function(data) { return data / (65536 / 500); }, sensorMpu9250AccConvert: function(data) { // Change /2 to match accel range...i.e. 16 g would be /16 return data / (32768 / 2); }, doAnimate: function() { 'use strict'; // Console.log('Animate..'); for (var t = 0; t < app.activeServices.length;t++) { app.activeServices[t].animateGraph(); } window.requestAnimFrame(app.doAnimate.bind(this)); } , connect: function(deviceId) { $('#results').slideUp(); console.log('Connect to ', deviceId); var onConnect = function(a) { var services = []; 'use strict'; console.log('A:', a); services = a.services; for (var t = 0; t < services.length; t++) { var ident = services[t].toUpperCase(); switch (ident) { case '180F': var batteryStat = new BATTERY(deviceId); batteryStat.startService(); // batteryStat.readBatteryState(); app.activeServices.push(batteryStat); break; case 'FFE0': var buttonState = new BUTTON(deviceId); buttonState.startService(); app.activeServices.push(buttonState); break; case 'F000AA80-0451-4000-B000-000000000000': var cc2650_accel = new CC2650_ACCEL(deviceId); cc2650_accel.startService(); app.activeServices.push(cc2650_accel); break; case 'F000AA40-0451-4000-B000-000000000000': var cc2650_bar = new CC2650_BAR(deviceId); cc2650_bar.startService(); app.activeServices.push(cc2650_bar); break; case 'F000AA70-0451-4000-B000-000000000000': var cc2650_lux = new CC2650_LUX(deviceId); cc2650_lux.startService(); app.activeServices.push(cc2650_lux); break; case 'F000AA00-0451-4000-B000-000000000000': var cc2650_tmp = new CC2650_TMP(deviceId); cc2650_tmp.startService(); app.activeServices.push(cc2650_tmp); break; case 'F000AA20-0451-4000-B000-000000000000': var cc2650_hum = new CC2650_HUM(deviceId); cc2650_hum.startService(); app.activeServices.push(cc2650_hum); break; default: console.error('Unknown service: ', ident); } } // Starting animation.. window.requestAnimFrame(app.doAnimate.bind(this)); }; ble.connect(deviceId, onConnect, function(e) { 'use strict'; console.error(e); }); }, onError: function(reason) { console.error('ERROR: ' + reason); // Real apps should use notification.alert }, updateGyro: function(g) { 'use strict'; } }; window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; })(); app.initialize();