-
Notifications
You must be signed in to change notification settings - Fork 13
/
index.html
210 lines (172 loc) · 5.42 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<html>
<head>
<title>WP6003 Air Box Reader</title>
<meta name="viewport" content="width=480">
</head>
<body >
<h1>WP6003 Air Box Reader</h1>
<h2>VSON WP6003 Bluetooth APP Air Quality Detector for PM Formaldehyde TVOC</h2>
<div style="text-align: center;"><img src="vson-wp6003.jpg" width="300" /></div>
<fieldset>
<legend>Read data:</legend>
<button onclick="enableNotifications()">Enable notifications</button></br>
<button onclick="readData()">Read sensor data</button></br>
<button onclick="sendCommand()">Send command</button></br>
<input type="checkbox" id="autoRefresh" name="autoRefresh" onclick="autoRefresh()">
<label for="autoRefresh">Auto refresh values (30s)</label>
</fieldset>
<fieldset>
<legend>Sensor calibration:</legend>
<div style="font-size: 14px">
Before sensor calibration procedure please follow these instructions:
<ol>
<li>Take the device to a place with fresh air (such as balcony, outdoor);
<li>Keep the device runnin for more than 10 minutes; then take it indoor
</ol>
</div>
<button onclick="calibrate()">Calibrate</button>
</fieldset>
<fieldset>
<legend>Console:</legend>
<div id="console"></div>
</fieldset>
</br></br>
<p>
<b>Links: </b>
<li><a href="http://www.vson.com.cn/English/Product/3614894931.html" target="_blank" rel="noopener">Official website</a>
<li><a href="https://play.google.com/store/apps/details?id=com.vson.smarthome" target="_blank" rel="noopener">Official app</a>
<li>Based on code <a href="https://github.com/zu2/wp6003" target="_blank" rel="noopener">zu2/wp6003</a>
</p>
<p>Use chrome developers tools for connection issues <a href="chrome://bluetooth-internals/#devices/">chrome://bluetooth-internals/#devices/</a></p>
<script>
const SENSOR_SERVICE = '0000fff0-0000-1000-8000-00805f9b34fb';
const SENSOR_WRITE = '0000fff1-0000-1000-8000-00805f9b34fb';
const SENSOR_READ = '0000fff4-0000-1000-8000-00805f9b34fb';
var service;
var readChar;
var writeChar;
var autoRefreshInterval;
async function initConnection() {
if(service != null)
return;
try {
service = await getService(SENSOR_SERVICE);
writeChar = await service.getCharacteristic(SENSOR_WRITE);
readChar = await service.getCharacteristic(SENSOR_READ);
} catch(error) {
console.error(error);
log('BT connection error. Please retry or refresh the browser.');
}
}
async function enableNotifications() {
await initConnection();
log('Enabaling notifications');
await readChar.startNotifications();
readChar.addEventListener('characteristicvaluechanged', handleNotifications);
log('Waiting on data... may take some time on a first read');
}
async function readData() {
await initConnection();
await writeChar.writeValue(Uint8Array.of(0xAB));
}
async function sendCommand() {
await initConnection();
let command = prompt('Enter a command in HEX');
await writeChar.writeValue(fromHexString(command));
}
async function calibrate() {
await initConnection();
await writeChar.writeValue(Uint8Array.of(0xAD));
log('Calabration started');
}
function autoRefresh() {
if(autoRefreshInterval) {
clearTimeout(autoRefreshInterval);
log('Auto refresh disabled');
} else {
autoRefreshInterval = setInterval(() => readData(), 30000);
log('Auto refresh enabled');
readData();
}
}
function handleNotifications(event) {
let value = event.target.value;
console.log(value);
console.log(toHexString(value));
let notificationType = value.getUint8(0);
switch(notificationType) {
case 0x0a:
case 0x0b:
logSensorData(value);
break;
default:
// nothing to decode
}
}
function logSensorData(value) {
try {
let time = new Date().toLocaleString();
let temp = value.getInt16(6) / 10.0;
let tvoc = value.getUint16(10) /1000.0;
let hcho = value.getUint16(12) /1000.0;
let co2 = value.getUint16(16);
log(`Time: ${time} <br/>
Temp: ${temp} <br/>
TVOC: ${tvoc} <br/>
HCHO: ${hcho} <br/>
CO2 : ${co2} <br/>`);
} catch(error) {
console.error(error);
log('Value parsing faild!');
}
}
async function getService(service) {
if (!('bluetooth' in navigator)) {
throw 'Bluetooth API not supported.';
}
let options = {
acceptAllDevices: true,
optionalServices: [service]
};
return navigator.bluetooth.requestDevice(options)
.then(function (device) {
log('Connecting...')
return device.gatt.connect();
})
.then(function (server) {
log('Getting primary service...')
return server.getPrimaryService(service);
});
}
function log(message) {
let element = document.getElementById('console');
console.log(message);
element.innerHTML = message;
}
//https://stackoverflow.com/questions/38987784/how-to-convert-a-hexadecimal-string-to-uint8array-and-back-in-javascript
function fromHexString(hexString) {
if(hexString.length === 0 || hexString.length % 2 !== 0){
throw new Error(`The string "${hexString}" is not valid hex.`)
}
return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}
function toHexString(data) {
let bytes = data;
if(data instanceof DataView) {
bytes = new Uint8Array(data.buffer);
}
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
}
</script>
<style>
body {
max-width: 400px;
margin: 0 auto;
font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;
}
fieldset > * {
margin: 5px;
}
</style>
</body>
</html>