6 changed files with 318 additions and 87 deletions
@ -1,21 +1,28 @@ |
|||||||
import { BaseInterface } from "./base"; |
import { BaseInterface } from "./base"; |
||||||
import dbus from "dbus-next"; |
import dbus, { interface as iface } from "dbus-next"; |
||||||
|
const { property, method } = iface; |
||||||
|
|
||||||
export class Advertisement extends BaseInterface { |
export class Advertisement extends BaseInterface { |
||||||
@dbus.interface.property({ signature: "s" }) |
@property({ signature: "s" }) |
||||||
Type = "peripheral"; |
Type = "peripheral"; |
||||||
|
|
||||||
@dbus.interface.property({ signature: "s" }) |
@property({ signature: "s" }) |
||||||
LocalName = "TestDevice"; |
LocalName = "TestDevice"; |
||||||
|
|
||||||
@dbus.interface.property({ signature: "as" }) |
@property({ signature: "as" }) |
||||||
ServiceUUIDs = ["139fc001-a4ed-11ed-b9df-0242ac120003"]; |
ServiceUUIDs = []; |
||||||
//ServiceUUIDs = ["1816", "1818", "1826"];
|
|
||||||
|
|
||||||
@dbus.interface.method({ inSignature: "", outSignature: "" }) |
@property({ signature: "a{sv}" }) |
||||||
|
ServiceData = { 1826: new dbus.Variant("ay", [0x01, 0x20, 0x00]) }; |
||||||
|
|
||||||
|
@property({ signature: "as" }) |
||||||
|
Includes = ["tx-power"]; |
||||||
|
|
||||||
|
@method({ inSignature: "", outSignature: "" }) |
||||||
Release() {} |
Release() {} |
||||||
|
|
||||||
constructor(bus, path) { |
constructor(bus, path, uuids) { |
||||||
super(bus, path, "org.bluez.LEAdvertisement1"); |
super(bus, path, "org.bluez.LEAdvertisement1"); |
||||||
|
this.ServiceUUIDs = uuids; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,137 @@ |
|||||||
|
import { BaseInterface } from "./base"; |
||||||
|
import { BaseService, BaseCharacteritic, BaseApplication } from "./bluez-gatt"; |
||||||
|
|
||||||
|
export class FTMSService extends BaseService { |
||||||
|
UUID = "00001826-0000-1000-8000-00805f9b34fb"; |
||||||
|
Primary = true; |
||||||
|
|
||||||
|
constructor(bus, path) { |
||||||
|
super(bus, path); |
||||||
|
this.addCharacteristic(new FTMSFeature(bus, this.path + "/char0", this)); |
||||||
|
this.addCharacteristic(new IndoorBikeData(bus, this.path + "/char1", this)); |
||||||
|
this.addCharacteristic( |
||||||
|
new FTMSControlPoint(bus, this.path + "/char2", this) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class FTMSFeature extends BaseCharacteritic { |
||||||
|
constructor(bus, path, service) { |
||||||
|
super(bus, path, service, "00002acc-0000-1000-8000-00805f9b34fb", ["read"]); |
||||||
|
} |
||||||
|
|
||||||
|
read() { |
||||||
|
console.log("read FTMS Feature"); |
||||||
|
// 32bits features + 32bits target supported
|
||||||
|
// 0x0a 0x44 = feature: cadence (1), inclinaison (3), hr (10), power (14)
|
||||||
|
// 0x0a 0x20 = target: inclinaison (1), power (3), simulation parameters (13)
|
||||||
|
return [0x0a, 0x44, 0x00, 0x00, 0x0a, 0x20, 0x00, 0x00]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class FTMSControlPoint extends BaseCharacteritic { |
||||||
|
wind = 0; |
||||||
|
grade = 0; |
||||||
|
crr = 0; |
||||||
|
cw = 0; |
||||||
|
|
||||||
|
constructor(bus, path, service) { |
||||||
|
super(bus, path, service, "00002ad9-0000-1000-8000-00805f9b34fb", [ |
||||||
|
"write", |
||||||
|
"indicate", |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
write(value, options) { |
||||||
|
console.log("FTMS Write Control Point"); |
||||||
|
console.log(value); |
||||||
|
const arraybuf = value.buffer.slice( |
||||||
|
value.byteOffset, |
||||||
|
value.byteOffset + value.byteLength |
||||||
|
); |
||||||
|
const view = new DataView(arraybuf); |
||||||
|
|
||||||
|
if (value[0] === 0x00) { |
||||||
|
// take control
|
||||||
|
this.notify([0x80, 0x00, 0x01]); |
||||||
|
} else if (value[0] === 0x01) { |
||||||
|
// reset machine
|
||||||
|
this.notify([0x80, 0x01, 0x01]); |
||||||
|
} else if (value[0] === 0x07) { |
||||||
|
// start/resume
|
||||||
|
this.notify([0x80, 0x07, 0x01]); |
||||||
|
} else if (value[0] === 0x11) { |
||||||
|
// set simulations param (wind, grade, etc)
|
||||||
|
const wind = view.getInt16(1, true); |
||||||
|
const grade = view.getInt16(3, true); |
||||||
|
const crr = view.getUint8(5); |
||||||
|
const cw = view.getUint8(6); |
||||||
|
|
||||||
|
this.wind = wind * 0.001; |
||||||
|
this.grade = grade * 0.01; |
||||||
|
this.crr = crr * 0.001; |
||||||
|
this.cw = cw * 0.01; |
||||||
|
|
||||||
|
console.log(wind * 0.001); |
||||||
|
console.log(grade * 0.01); |
||||||
|
console.log(crr * 0.0001); |
||||||
|
console.log(cw * 0.01); |
||||||
|
|
||||||
|
this.notify([0x80, 0x11, 0x01]); |
||||||
|
} else { |
||||||
|
console.log("FTMS Control point op code " + value[0] + "not supported"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
startNotify() { |
||||||
|
console.log("FTMS Start Notify CP"); |
||||||
|
} |
||||||
|
|
||||||
|
stopNotify() { |
||||||
|
console.log("FTMS Stop Notify CP"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class IndoorBikeData extends BaseCharacteritic { |
||||||
|
constructor(bus, path, service) { |
||||||
|
super(bus, path, service, "00002ad2-0000-1000-8000-00805f9b34fb", [ |
||||||
|
"notify", |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
startNotify() { |
||||||
|
if (this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = true; |
||||||
|
|
||||||
|
this.interval = setInterval(() => { |
||||||
|
console.log("FTMS Notif Indoor Bike"); |
||||||
|
// 0x44 0x02 = fields included: cadence (2), power (6), hr (9)
|
||||||
|
// 0x00 0x00 = average speed, always included
|
||||||
|
// cadence is *2 (resolution 0.5 s^-1)
|
||||||
|
this.notify([ |
||||||
|
0x44, |
||||||
|
0x02, |
||||||
|
0x00, |
||||||
|
0x00, |
||||||
|
Math.round(160 + Math.random() * 10), |
||||||
|
0x00, |
||||||
|
Math.round(160 + Math.random() * 40), |
||||||
|
0x00, |
||||||
|
Math.round(120 + Math.random() * 10), |
||||||
|
]); |
||||||
|
}, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
stopNotify() { |
||||||
|
if (!this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = false; |
||||||
|
|
||||||
|
clearInterval(this.interval); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
import { BaseInterface } from "./base"; |
||||||
|
import { BaseService, BaseCharacteritic, BaseApplication } from "./bluez-gatt"; |
||||||
|
|
||||||
|
export class HRService extends BaseService { |
||||||
|
UUID = "0000180d-0000-1000-8000-00805f9b34fb"; |
||||||
|
Primary = true; |
||||||
|
|
||||||
|
constructor(bus, path) { |
||||||
|
super(bus, path); |
||||||
|
this.addCharacteristic(new HRMeasure(bus, this.path + "/char0", this)); |
||||||
|
//this.addCharacteristic(new WriteChar(bus, "/eu/atoy/hrservice/char1", this));
|
||||||
|
//this.addCharacteristic(new NotifChar(bus, "/eu/atoy/hrservice/char2", this));
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class HRMeasure extends BaseCharacteritic { |
||||||
|
constructor(bus, path, service) { |
||||||
|
super(bus, path, service, "00002a37-0000-1000-8000-00805f9b34fb", [ |
||||||
|
"notify", |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
startNotify() { |
||||||
|
if (this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = true; |
||||||
|
|
||||||
|
this.interval = setInterval(() => { |
||||||
|
console.log("HR Notify"); |
||||||
|
this.notify([0x06, Math.round(120 + Math.random() * 10)]); |
||||||
|
}, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
stopNotify() { |
||||||
|
if (!this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = false; |
||||||
|
|
||||||
|
clearInterval(this.interval); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
import { BaseInterface } from "./base"; |
||||||
|
import { BaseService, BaseCharacteritic, BaseApplication } from "./bluez-gatt"; |
||||||
|
|
||||||
|
export class PWRService extends BaseService { |
||||||
|
UUID = "00001818-0000-1000-8000-00805f9b34fb"; |
||||||
|
Primary = true; |
||||||
|
|
||||||
|
constructor(bus, path) { |
||||||
|
super(bus, path); |
||||||
|
this.addCharacteristic(new PWRMeasure(bus, this.path + "/char0", this)); |
||||||
|
//this.addCharacteristic(new WriteChar(bus, "/eu/atoy/hrservice/char1", this));
|
||||||
|
//this.addCharacteristic(new NotifChar(bus, "/eu/atoy/hrservice/char2", this));
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class PWRMeasure extends BaseCharacteritic { |
||||||
|
constructor(bus, path, service) { |
||||||
|
super(bus, path, service, "00002a63-0000-1000-8000-00805f9b34fb", [ |
||||||
|
"notify", |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
startNotify() { |
||||||
|
if (this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = true; |
||||||
|
|
||||||
|
this.interval = setInterval(() => { |
||||||
|
console.log("Power Notify"); |
||||||
|
this.notify([0x00, 0x00, Math.round(170 + Math.random() * 30), 0x00]); |
||||||
|
}, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
stopNotify() { |
||||||
|
if (!this.Notifying) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.Notifying = false; |
||||||
|
|
||||||
|
clearInterval(this.interval); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue