/* Bluetooth */ function ab2str(buf) { return String.fromCharCode.apply(null, new Uint8Array(buf)); } function str2ab(str) { var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char var bufView = new Uint16Array(buf); for (var i=0, strLen=str.length; i 0 ) { line = receive_buffer.substring( 0, p - 1 ); // console.log( line ); receive_buffer = receive_buffer.substring( p + 1, receive_buffer.length ); // Keep all received lines in a variable // received_log.concat( line + "\r\n" ); mecoffee_terminal( line ); i++; if( i > 25 ) break; } // Disconnect timer if( disconnect_timer ) clearTimeout( disconnect_timer ); disconnect_timer = setTimeout( onReceiveTimeout, 5000 ); }; var onConnectedCallback = function() { if (chrome.runtime.lastError) console.log("Connection failed: " + chrome.runtime.lastError.message); //else //chrome.bluetoothSocket.onReceive.addListener( onReceive ); }; chrome.bluetoothSocket.onReceive.addListener( onReceive ); var mecoffee_socketId; function mecoffee_connect( device_name ) { var device; chrome.bluetooth.getDevices(function(devices) { for (var i = 0; i < devices.length; i++) { if( devices[i].name == device_name ) { console.log(devices[i].name); device = devices[i]; var uuid = '1106'; uuid= "00001101-0000-1000-8000-00805F9B34FB"; chrome.bluetoothSocket.create( function(createInfo) { mecoffee_socketId = createInfo.socketId; chrome.bluetoothSocket.connect(createInfo.socketId, device.address, uuid, onConnectedCallback); chrome.bluetoothSocket.onAcceptError.addListener( function() { console.log('onaccepterror'); } ); chrome.bluetoothSocket.onReceiveError.addListener( function() { console.log('onreceiveerror'); } ); chrome.runtime.sendMessage( { type: 'socket', socket: mecoffee_socketId }, function(response) { console.log( 'socket antwoord' ); } ); }); break; } } }); } function mecoffee_disconnect( ) { chrome.bluetoothSocket.disconnect( mecoffee_socketId ); chrome.bluetoothSocket.close( mecoffee_socketId ); mecoffee_timestamp_start = null; } function mecoffee_bt_enum_devices( ) { if( !chrome || !chrome.bluetooth ) return ; chrome.bluetooth.getDevices(function(devices) { $('#device option[value="none"]').remove() for (var i = 0; i < devices.length; i++) { $('#devices').append(''); } $('#device')[0].selectedIndex = 0; $('#device').selectpicker("refresh") }); } /* meCoffee protocol */ var mecoffee_timestamp_start; function mecoffee_terminal_tmp( items ) { var ts = parseInt( items[1] ); var tmp = parseInt( items[3] ) / 100; var tmp2 = parseInt( items[4] ) / 100; var tmpsp = parseInt( items[2] ) / 100; if( !mecoffee_timestamp_start && mecoffee_timestamp_start != 0 ) { mecoffee_timestamp_start = ts ; mecoffee_terminal_write( "\r\ncmd dump OK\r\n" ); } if( ts - mecoffee_timestamp_start == 2 ) { var d = new Date(), e = new Date(d); var msSinceMidnight = e - d.setHours(0,0,0,0); mecoffee_terminal_write( "\r\ncmd clock set " + parseInt( msSinceMidnight / 1000 ) + " OK\r\n" ); console.log( 'clock set to ' + parseInt( msSinceMidnight / 1000 ) ); } if( ts - mecoffee_timestamp_start == 5 ) { mecoffee_terminal_write( "\r\ncmd uname OK\r\n"); } graph.tick( tmp, tmp2, tmpsp, ts - mecoffee_timestamp_start ); //mecoffee_cur_tmp = tmp; //mecoffee_cur_sp = tmpsp; document.querySelector('#mc_tmp').value = tmp; document.querySelector('#mc_tmpsp').value = tmpsp; } var gauge_boiler; function mecoffee_terminal_pid( items ) { var pid = parseInt( items[1] ) + parseInt( items[2] ) + parseInt( items[3] ); gauge_boiler.write( pid / 65536 * 100); } var gauge_shottimer; var gauge_shottimer_time; var gauge_shottimer_timeout; var gauge_shottimer_interval = null; function mecoffee_terminal_shot( items ) { var value = parseInt( items[ items.length - 2 ] ); var speed = 1; if( mecoffee_demo_interval > 0 ) { speed = $('#demo_speed')[0].value; } if( gauge_shottimer_timeout ) { clearTimeout( gauge_shottimer_timeout ); gauge_shottimer_timeout = null; } $('#gauge_shottimer').show(); // start interval to update shot timer every sec if( value == 0 && gauge_shottimer_interval == null ) { gauge_shottimer_time = 0; console.log( 'started interval ' ); gauge_shottimer_interval = setInterval( function() { gauge_shottimer.write( ++gauge_shottimer_time ); //console.log( 'update shot timer to ' + gauge_shottimer_time ); }, 1000 / speed ); } // clean up shot timer after 30 secs if( value > 0 ) { if( gauge_shottimer_interval ) { clearInterval( gauge_shottimer_interval ); gauge_shottimer_interval = null; gauge_shottimer.write( value / 1000.0 ); } gauge_shottimer_timeout = setTimeout( function() { gauge_shottimer.write( 0 ); $('#gauge_shottimer').hide(); gauge_shottimer_timeout = null; }, Math.min( 30000, 30000 / speed ) ); } gauge_shottimer.write( value / 1000 ); } function mecoffee_terminal_cmd( items ) { if( items[1] == 'get' ) { var elems = $( '#mc_' + items[2] ); var value = items[3]; if( elems.length == 0 ) { console.log( '#mc_' + items[2] + ' not found' ); return; } var elem = elems[0]; var scale = elem.getAttribute('data-scale'); if( scale ) value /= scale; if( elems[0].className.indexOf('timepicker') >= 0) { var hour = Math.floor( value / ( 60*60 ) ); var min = value % ( 60*60 ); elems[0].value = hour + ':' + Math.floor( min / 60 ); return; } if( elems[0].type == "checkbox" ) { elems.bootstrapSwitch('state' , items[3] == "1" ); return; } if( elems.attr( 'data-slider-value' ) ) { elems.slider( 'setValue', parseInt( items[3] ) ); return; } elems[0].value = value; } } function mecoffee_terminal_uname( line ) { legacy = line.indexOf( 'V4' ) > 0; if( legacy ) { console.log( 'meCoffee is legacy ( V4 )' ); $('#mc_pistrt')[0].setAttribute( 'data-scale', 1 ); $('#mc_pistrt')[0].setAttribute( 'data-slider-step', 1 ); $('#mc_pistrt').slider( 'destroy' ); $('#mc_pistrt').slider( ); $( '#mc_pistrt' ).change( mecoffee_control_change ); $('#mc_piprd')[0].setAttribute( 'data-scale', 1 ); $('#mc_piprd')[0].setAttribute( 'data-slider-step', 1 ); $('#mc_piprd').slider( 'destroy' ); $('#mc_piprd').slider( ); $( '#mc_piprd' ).change( mecoffee_control_change ); } else { console.log( 'meCoffee has newish firmware' ); $('#mc_pistrt')[0].setAttribute( 'data-scale', 1000 ); $('#mc_pistrt')[0].setAttribute( 'data-slider-step', "0.1" ); $('#mc_pistrt').slider( 'destroy' ); $('#mc_pistrt').slider( ); $( '#mc_pistrt' ).change( mecoffee_control_change ); $('#mc_piprd')[0].setAttribute( 'data-scale', 1000 ); $('#mc_piprd')[0].setAttribute( 'data-slider-step', 0.1 ); $('#mc_piprd').slider( 'destroy' ); $('#mc_piprd').slider( ); $( '#mc_piprd' ).change( mecoffee_control_change ); } // $('.slider').destroy(); // $('.slider').slider(); // $('#mc_pistrt')[0].setAttribute( 'data-scala', 1000 ); } function mecoffee_terminal( line ) { received_log = received_log.concat( line + "\n" ); var items = line.split(' '); if( items[ items.length - 1 ] != 'OK' ) return; if( items[0] == 'tmp' ) { mecoffee_terminal_tmp( items ); } if( items[1] == 'uname' ) { mecoffee_terminal_uname( line ); return; } if( items[0] == 'cmd' ) mecoffee_terminal_cmd( items ); if( items[0] == 'pid' ) mecoffee_terminal_pid( items ); if( items[0] == 'sht' ) mecoffee_terminal_shot( items ); } function mecoffee_terminal_write( line ) { if( !mecoffee_socketId ) return ; chrome.bluetoothSocket.send( mecoffee_socketId, str2ab( line ), function(bytes_sent) { if (chrome.runtime.lastError) { console.log("Send failed: " + chrome.runtime.lastError.message); } else { console.log("Sent " + bytes_sent + " bytes") } }); } /* Demo */ var mecoffee_demo_log = ""; var mecoffee_demo_interval = 0; var mecoffee_demo_cnt = 0; function mecoffee_demo_timer( ) { if( mecoffee_demo_cnt >= mecoffee_demo_log.length ) { mecoffee_demo_stop(); //clearInterval( mecoffee_demo_interval ); // TODO: change start button state //mecoffee_demo_cnt = 0; } for( ; mecoffee_demo_cnt < mecoffee_demo_log.length ; mecoffee_demo_cnt++ ) { var line = mecoffee_demo_log[mecoffee_demo_cnt]; mecoffee_terminal( line ); if( line.indexOf('tmp ') == 0 ) { mecoffee_demo_cnt++; break; } } } function mecoffee_demo( log ) { log = log.replace( "share_", "" ); $.get( "https://mecoffee.nl/share/logs/" + log, function( data ) { mecoffee_demo_log = data.split("\n"); mecoffee_demo_interval = setInterval( mecoffee_demo_timer, 1000 / $('#demo_speed')[0].value ); }); return false; } function mecoffee_demo_speed( e ) { if( !mecoffee_demo_interval ) return ; clearInterval( mecoffee_demo_interval ); mecoffee_demo_interval = setInterval( mecoffee_demo_timer, 1000 / e.target.value ); } function mecoffee_demo_stop( e ) { if( mecoffee_demo_interval ) clearInterval( mecoffee_demo_interval ); mecoffee_demo_interval = 0; mecoffee_demo_cnt = 0; $('#start_icon')[0].className = "glyphicon glyphicon-play"; $('#start_label')[0].innerHTML = "Start"; start.className ="btn btn-primary"; } /* App */ function mecoffee_start( e ) { var start = $('#start')[0]; if( start.className.indexOf("danger") > 0 ) { // Stop start.className ="btn btn-primary"; $('#start_icon')[0].className = "glyphicon glyphicon-play"; $('#start_label')[0].innerHTML = "Start"; mecoffee_disconnect( ); mecoffee_demo_stop( ); } else { // Start start.className ="btn btn-danger"; $('#start_icon')[0].className = "glyphicon glyphicon-stop"; $('#start_label')[0].innerHTML = "Stop"; var value = $('#device')[0].value; if( value.indexOf('device_') == 0 ) mecoffee_connect( value.replace( 'device_', '' ) ); if( value.indexOf('share_') == 0 ) mecoffee_demo( value ); } return false; } function mecoffee_share( e ) { $.post( "https://mecoffee.nl/share/post.php", { logfile: received_log }, function( data ) { $( "#share_result" ).html( '' + data + ''); window.open( data, "_blank" ); }); return false; } function mecoffee_control_change( e ) { var elem = e.target; if( elem.id.indexOf('mc_') != 0 ) return; var value = elem.value; if( e.value && e.value.newValue ) value = e.value.newValue; if( elem.type == "checkbox" ) value = elem.checked ? 1 : 0; if( value.indexOf && value.indexOf(':') > 0 ) { var p = value.split(':'); value = 60*60*p[0] + 60*p[1]; } var scale = elem.getAttribute('data-scale'); if( scale ) value *= scale; var line = "\r\ncmd set " + elem.id.replace( "mc_", "" ) + " " + value + " OK\r\n"; console.log( line ); mecoffee_terminal_write( line ); } var graph; function mecoffee_app_startup() { //document.querySelector("#doit").addEventListener( 'click', doit_doit ); $('#start').click( mecoffee_start ); $('#share').click( mecoffee_share ); window.addEventListener("resize", function() { graph.draw(); }); graph = new meCoffeeGraph(); graph.draw(); $("input[type='checkbox']").bootstrapSwitch( { onSwitchChange: function ( e ) { mecoffee_control_change( e ); }}); // slider_pistrt = new Slider( '#mc_pistrt' ); $('.slider').slider(); $('.timepicker').timepicker( { showMeridian: false } ); $( ".target" ).change( mecoffee_control_change ); $( "#demo_speed").change( mecoffee_demo_speed ); //var gauge_boiler = new Gauge(); gauge_boiler.draw( $('#gauge_boiler')[0] , { label: 'Boiler %' } ); gauge_shottimer = new Gauge(); gauge_shottimer.draw( $('#gauge_shottimer')[0] , { label: 'Shot (s)', max: 30 } ); mecoffee_bt_enum_devices( ); } window.addEventListener("DOMContentLoaded", mecoffee_app_startup );