|
|
import { Core } from "./device/index.js";
|
|
|
import { requestHRSensor } from "./ble/index.js";
|
|
|
|
|
|
const startButton = document.getElementById("start");
|
|
|
const hrButton = document.getElementById("hr");
|
|
|
|
|
|
hrButton.addEventListener("click", async () => {
|
|
|
function parseHeartRate(dataView) {
|
|
|
// Flags are in the first byte
|
|
|
const flags = dataView.getUint8(0);
|
|
|
const hrFormatUint16 = flags & 0x01; // 0 = 8‑bit, 1 = 16‑bit
|
|
|
|
|
|
if (hrFormatUint16) {
|
|
|
return dataView.getUint16(1, /*littleEndian=*/ true);
|
|
|
}
|
|
|
return dataView.getUint8(1);
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const device = await requestHRSensor();
|
|
|
console.log("D", device);
|
|
|
if (!device) return;
|
|
|
const server = await device.gatt.connect();
|
|
|
|
|
|
const partialCore = new Core({
|
|
|
supportsBattery: true,
|
|
|
supportsManufacturerData: true,
|
|
|
gattServices: [
|
|
|
0x180a, // Device Information
|
|
|
0x180d, // Heart Rate
|
|
|
],
|
|
|
gattCharacteristics: [
|
|
|
0x2a29, // Manufacturer Name
|
|
|
0x2a24, // Model Number
|
|
|
0x2a25, // Serial number
|
|
|
0x2a26, // Firmware version
|
|
|
0x2a37, // Heart Rate Measurement
|
|
|
],
|
|
|
});
|
|
|
|
|
|
const info = await server.getPrimaryService(partialCore.gattServices[0]);
|
|
|
const [manufacturer, model, firmwareVersion] = await Promise.all([
|
|
|
info.getCharacteristic(0x2a29).then((c) => c.readValue()),
|
|
|
info.getCharacteristic(0x2a24).then((c) => c.readValue()),
|
|
|
info.getCharacteristic(0x2a26).then((c) => c.readValue()),
|
|
|
//info.getCharacteristic(0x2a25).then((c) => c.readValue()),
|
|
|
]);
|
|
|
|
|
|
// const [infoService, hrService] = await Promise.all([
|
|
|
// server.getPrimaryService(0x180a), // Device Information
|
|
|
// server.getPrimaryService(0x180d), // Heart Rate
|
|
|
// ]);
|
|
|
|
|
|
// const [manufVal, modelVal, hrChar] = await Promise.all([
|
|
|
// infoService.getCharacteristic(0x2a29).then((c) => c.readValue()), // Manufacturer Name (0x2A29)
|
|
|
// infoService.getCharacteristic(0x2a24).then((c) => c.readValue()), // Model Number (0x2A24)
|
|
|
// hrService
|
|
|
// .getCharacteristic(0x2a37)
|
|
|
// .then((c) => c.startNotifications().then(() => c)), // Heart Rate Measurement
|
|
|
// ]);
|
|
|
|
|
|
const dec = new TextDecoder("utf-8");
|
|
|
|
|
|
const core = partialCore.merge({
|
|
|
manufacturer: dec.decode(manufacturer),
|
|
|
model: dec.decode(model),
|
|
|
firmwareVersion: dec.decode(firmwareVersion),
|
|
|
//serial: dec.decode(serial),
|
|
|
});
|
|
|
|
|
|
console.log(core);
|
|
|
|
|
|
// hrChar.addEventListener("characteristicvaluechanged", (ev) =>
|
|
|
// console.log("❤️", parseHeartRate(ev.target.value), "bpm"),
|
|
|
// );
|
|
|
} catch (error) {
|
|
|
console.error(error);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
const devices = await navigator.bluetooth.getDevices();
|
|
|
console.log("CONNECTED", devices);
|
|
|
|
|
|
// devices.forEach(async (device) => {
|
|
|
// const gattServer = await device.gatt.connect();
|
|
|
// const primaryService = await gattServer.getPrimaryService(0x1800);
|
|
|
// const MANUFACTURER_UUID = 0x2a29;
|
|
|
// const MODEL_UUID = 0x2a0a;
|
|
|
// const char = await primaryService.getCharacteristic(MODEL_UUID);
|
|
|
// const val = await char.readValue();
|
|
|
// //const char2 = await primaryService.getCharacteristic(MODEL_UUID);
|
|
|
// //const val2 = await char2.readValue();
|
|
|
// const decoder = new TextDecoder("utf-8");
|
|
|
// const manufacturer = decoder.decode(val);
|
|
|
// //const model = decoder.decode(val2);
|
|
|
// console.log("Manufacturer:", manufacturer);
|
|
|
// });
|
|
|
|
|
|
startButton.addEventListener("click", async () => {
|
|
|
try {
|
|
|
const devices = await navigator.bluetooth.getDevices();
|
|
|
console.log(2, devices);
|
|
|
const device = await navigator.bluetooth.requestDevice({
|
|
|
filters: [{ services: [0x180d] }],
|
|
|
optionalServices: [
|
|
|
0x1818, // Cycling Power – watts, torque, crank torque, etc.
|
|
|
0x1816, // Cycling Speed & Cadence – speed, cadence, distance
|
|
|
0x180d, // Heart Rate – optional, if the trainer includes an HR sensor
|
|
|
0x180f, // Battery Service – trainer battery level (if battery‑powered)
|
|
|
0x180a, // Device Information – manufacturer, model, firmware version
|
|
|
0x1800,
|
|
|
// add any custom 128‑bit UUIDs as strings, e.g.
|
|
|
// '0000abcd-0000-1000-8000-00805f9b34fb'
|
|
|
],
|
|
|
acceptAllDevices: true,
|
|
|
});
|
|
|
|
|
|
const server = await device.gatt.connect();
|
|
|
|
|
|
console.log(await server.getPrimaryServices());
|
|
|
} catch (error) {
|
|
|
console.log(error);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// startButton.addEventListener("click", async () => {
|
|
|
// try {
|
|
|
// const devices = await navigator.bluetooth.getDevices();
|
|
|
// console.log(2, devices);
|
|
|
// const device = await navigator.bluetooth.requestDevice({
|
|
|
// //filters: [{ services: ["heart_rate"] }],
|
|
|
// optionalServices: [
|
|
|
// 0x1818, // Cycling Power – watts, torque, crank torque, etc.
|
|
|
// 0x1816, // Cycling Speed & Cadence – speed, cadence, distance
|
|
|
// 0x180d, // Heart Rate – optional, if the trainer includes an HR sensor
|
|
|
// 0x180f, // Battery Service – trainer battery level (if battery‑powered)
|
|
|
// 0x180a, // Device Information – manufacturer, model, firmware version
|
|
|
// 0x1800,
|
|
|
// // add any custom 128‑bit UUIDs as strings, e.g.
|
|
|
// // '0000abcd-0000-1000-8000-00805f9b34fb'
|
|
|
// ],
|
|
|
// acceptAllDevices: true,
|
|
|
// });
|
|
|
|
|
|
// const server = await device.gatt.connect();
|
|
|
|
|
|
// console.log(await server.getPrimaryServices());
|
|
|
// } catch (error) {
|
|
|
// console.log(error);
|
|
|
// }
|
|
|
// });
|