BBQ Thermometer with the Web Bluetooth API
* {
box-sizing: border-box;
}
:root {
--ice-cold: #2196f3;
--medium: #9e9e9e;
--boiling-hot: #f45236;
}
body, html {
margin: 0;
padding: 1em;
width: 100%;
height: 100%;
}
html {
background-color: var(--medium);
}
.cold {
background-color: var(--ice-cold);
}
.hot {
background-color: var(--boiling-hot);
}
button {
font-size: 2em;
display: block;
width: 100%;
border: 0;
}
.error-logs {
background: #990000;
}
{
"name": "bbq",
"version": "1.0.0",
"description": "",
"main": "app.js",
"dependencies": {
"http-server": "^0.9.0"
},
"devDependencies": {},
"scripts": {
"start": "http-server --ssl"
},
"author": "",
"license": "ISC"
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
<style>
</style>
</head>
<body>
<button class="bt">Scan</button>
<h1 class="value"></h1>
<h1>Sensor 1</h1>
<h2 class="status-1"></h2>
<h1>Sensor 2</h1>
<h2 class="status-2"></h2>
<p>
<pre class="logs"></pre>
</p>
<p>
<pre class="error-logs"></pre>
</p>
</body>
<script src="app.js?b"></script>
</html>
const display = document.querySelector('.value');
const status1 = document.querySelector('.status-1');
const status2 = document.querySelector('.status-2');
const logs = document.querySelector('.logs');
const errorLogs = document.querySelector('.error-logs');
const html = document.documentElement;
document.querySelector('.bt').addEventListener('click', handleClick);
const setBgColor = (temp) => {
const classList = [];
if (temp < 60) {
classList.push('cold');
}
if (temp > 110) {
classList.push('hot');
}
html.classList = classList;
};
const setupEventListener = (char, el) => {
char.addEventListener('characteristicvaluechanged', e => {
const { value } = e.target;
const temp = value.getUint16(12, true) / 10;
if (temp !== 3686.3) {
el.innerHTML = temp;
setBgColor(temp);
} else {
el.innerHTML = '----';
}
});
};
let customService = '2899fe00-c277-48a8-91cb-b29ab0f01ac4';
const main = '28998e03-c277-48a8-91cb-b29ab0f01ac4';
const sensor1 = '28998e10-c277-48a8-91cb-b29ab0f01ac4';
const sensor2 = '28998e11-c277-48a8-91cb-b29ab0f01ac4';
const getSensor = (chars, uuid) => chars.find(c => c.uuid === uuid);
let chars;
function handleClick() {
console.log('clicked! accept all devices...');
navigator.bluetooth.requestDevice({
acceptAllDevices: true
//filters: [{ services: [customService] }]
})
.then(device => {
console.log('connected, have device');
return device.gatt.connect();
}).then(server => server.getPrimaryService(customService))
.then(service => service.getCharacteristics())
.then(cs => {
chars = cs;
const m = getSensor(chars, main);
return m.startNotifications();
})
.then(_ => getSensor(chars, sensor1).startNotifications())
.then(char1 => {
setupEventListener(char1, status1);
return getSensor(chars, sensor2).startNotifications();
})
.then(char2 => {
setupEventListener(char2, status2);
})
.then(_ => {
console.log('done');
})
.catch(error => {
console.error('failed', error);
alert(error) ;
});
}
window.onerror = (message, source, line, col, err) => {
alert(message);
console.error(message);
};
console.log = (...args) => {
args.forEach(arg => logs.innerHTML += `${arg}\n`);
};
console.error = (...args) => {
args.forEach(arg => errorLogs.innerHTML += `${arg}\n`);
};
I bought a Bluetooth-enabled BBQ thermometer on a daily-deal site a while back as a dumb impulse purchase.
To use it on a phone, you're supposed to download an app, but eff that noise.
I decided to see if I could get something working using the Web Bluetooth API. It's a little hacky, but it works! Tested on Chrome for Android, and OS X.
A few notes:
chrome://flags
.startNotifications()
in a particular ordernode_modules
*.pem