first Working FTMS
This commit is contained in:
+15
-8
@@ -1,21 +1,28 @@
|
||||
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 {
|
||||
@dbus.interface.property({ signature: "s" })
|
||||
@property({ signature: "s" })
|
||||
Type = "peripheral";
|
||||
|
||||
@dbus.interface.property({ signature: "s" })
|
||||
@property({ signature: "s" })
|
||||
LocalName = "TestDevice";
|
||||
|
||||
@dbus.interface.property({ signature: "as" })
|
||||
ServiceUUIDs = ["139fc001-a4ed-11ed-b9df-0242ac120003"];
|
||||
//ServiceUUIDs = ["1816", "1818", "1826"];
|
||||
@property({ signature: "as" })
|
||||
ServiceUUIDs = [];
|
||||
|
||||
@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() {}
|
||||
|
||||
constructor(bus, path) {
|
||||
constructor(bus, path, uuids) {
|
||||
super(bus, path, "org.bluez.LEAdvertisement1");
|
||||
this.ServiceUUIDs = uuids;
|
||||
}
|
||||
}
|
||||
|
||||
+48
-3
@@ -1,6 +1,35 @@
|
||||
import { BaseInterface } from "./base";
|
||||
import { Variant, interface as iface } from "dbus-next";
|
||||
const { method, property } = iface;
|
||||
const { method, property, Interface } = iface;
|
||||
|
||||
export class BaseApplication extends BaseInterface {
|
||||
services = [];
|
||||
|
||||
constructor(bus, path) {
|
||||
super(bus, path, "org.freedesktop.DBus.ObjectManager");
|
||||
}
|
||||
|
||||
addService(service) {
|
||||
this.services.push(service);
|
||||
}
|
||||
|
||||
@method({ outSignature: "a{oa{sa{sv}}}" })
|
||||
GetManagedObjects(val) {
|
||||
const reply = {};
|
||||
for (const service of this.services) {
|
||||
reply[service.path] = {
|
||||
[service.$name]: service.getProperties(),
|
||||
};
|
||||
|
||||
for (const char of service.characteristics) {
|
||||
reply[char.path] = {
|
||||
[char.$name]: char.getProperties(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseService extends BaseInterface {
|
||||
@property({ signature: "s" })
|
||||
@@ -46,6 +75,9 @@ export class BaseCharacteritic extends BaseInterface {
|
||||
@property({ signature: "b" })
|
||||
Notifying = false;
|
||||
|
||||
@property({ signature: "ay" })
|
||||
Value = [];
|
||||
|
||||
constructor(bus, path, service, uuid, flags) {
|
||||
super(bus, path, "org.bluez.GattCharacteristic1");
|
||||
this.UUID = uuid;
|
||||
@@ -73,12 +105,25 @@ export class BaseCharacteritic extends BaseInterface {
|
||||
|
||||
@method({})
|
||||
StartNotify() {
|
||||
console.log("not definied");
|
||||
if (this.startNotify) {
|
||||
this.startNotify();
|
||||
} else {
|
||||
console.log("not definied");
|
||||
}
|
||||
}
|
||||
|
||||
@method({})
|
||||
StopNotify() {
|
||||
console.log("not definied");
|
||||
if (this.stopNotify) {
|
||||
this.stopNotify();
|
||||
} else {
|
||||
console.log("not definied");
|
||||
}
|
||||
}
|
||||
|
||||
notify(value) {
|
||||
this.Value = value;
|
||||
Interface.emitPropertiesChanged(this, { Value: value });
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+28
-76
@@ -1,84 +1,29 @@
|
||||
import { systemBus, Variant, interface as iface } from "dbus-next";
|
||||
|
||||
const { Interface, method, property } = iface;
|
||||
|
||||
import { BaseService, BaseCharacteritic } from "./bluez-gatt";
|
||||
import { systemBus } from "dbus-next";
|
||||
import { BaseApplication } from "./bluez-gatt";
|
||||
import { Advertisement } from "./adv";
|
||||
import { BaseInterface } from "./base";
|
||||
import { Bluez } from "./bluez";
|
||||
import { HRService } from "./hr-service";
|
||||
import { PWRService } from "./power-service";
|
||||
import { FTMSService } from "./ftms-service";
|
||||
|
||||
class Application extends BaseInterface {
|
||||
constructor(bus, path) {
|
||||
super(bus, path, "org.freedesktop.DBus.ObjectManager");
|
||||
|
||||
const gattService = new MyService(bus, "/eu/atoy/service0");
|
||||
|
||||
this.services = [gattService];
|
||||
}
|
||||
|
||||
@method({ outSignature: "a{oa{sa{sv}}}" })
|
||||
GetManagedObjects(val) {
|
||||
const reply = {};
|
||||
for (const service of this.services) {
|
||||
reply[service.path] = {
|
||||
[service.$name]: service.getProperties(),
|
||||
};
|
||||
|
||||
for (const char of service.characteristics) {
|
||||
reply[char.path] = {
|
||||
[char.$name]: char.getProperties(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
class MyService extends BaseService {
|
||||
UUID = "139fc001-a4ed-11ed-b9df-0242ac120003";
|
||||
Primary = true;
|
||||
|
||||
@property({ signature: "ao" })
|
||||
get Characteristics() {
|
||||
return this.characteristics.map((c) => c.path);
|
||||
}
|
||||
|
||||
characteristics = [];
|
||||
|
||||
class BikeApplication extends BaseApplication {
|
||||
constructor(bus, path) {
|
||||
super(bus, path);
|
||||
this.addCharacteristic(new PowerChar(bus, "/eu/atoy/service0/char0", this));
|
||||
this.addCharacteristic(new WriteChar(bus, "/eu/atoy/service0/char1", this));
|
||||
}
|
||||
}
|
||||
|
||||
class PowerChar extends BaseCharacteritic {
|
||||
constructor(bus, path, service) {
|
||||
super(bus, path, service, "139fc002-a4ed-11ed-b9df-0242ac120003", ["read"]);
|
||||
}
|
||||
|
||||
read(options) {
|
||||
return [0xaa, 0xcc];
|
||||
}
|
||||
}
|
||||
|
||||
class WriteChar extends BaseCharacteritic {
|
||||
constructor(bus, path, service) {
|
||||
super(bus, path, service, "139fc003-a4ed-11ed-b9df-0242ac120003", [
|
||||
"write",
|
||||
]);
|
||||
}
|
||||
|
||||
write(value, options) {
|
||||
console.log(value);
|
||||
//this.addService(new HRService(bus, this.path + "/hrservice"));
|
||||
//this.addService(new PWRService(bus, this.path + "/pwrservice"));
|
||||
this.addService(new FTMSService(bus, this.path + "/ftmsservice"));
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const bus = systemBus();
|
||||
|
||||
const app = new Application(bus, "/eu/atoy");
|
||||
const adv = new Advertisement(bus, "/eu/atoy/advertising");
|
||||
const app = new BikeApplication(bus, "/eu/atoy");
|
||||
const adv = new Advertisement(bus, "/eu/atoy/advertising", [
|
||||
//"1818",
|
||||
//"180d",
|
||||
"1826",
|
||||
]);
|
||||
|
||||
await bus.requestName("eu.atoy");
|
||||
|
||||
@@ -86,10 +31,17 @@ async function main() {
|
||||
const leamgr = obj.getInterface("org.bluez.LEAdvertisingManager1");
|
||||
const gattmgr = obj.getInterface("org.bluez.GattManager1");
|
||||
|
||||
/*leamgr.RegisterAdvertisement("/eu/atoy/advertising", {});
|
||||
console.log("BLE Advertisement registered");
|
||||
gattmgr.RegisterApplication("/eu/atoy", {});
|
||||
console.log("BLE GATT application registered");*/
|
||||
try {
|
||||
await leamgr.RegisterAdvertisement("/eu/atoy/advertising", {});
|
||||
console.log("BLE Advertisement registered");
|
||||
} catch (e) {
|
||||
console.log("Unable to register BLE Advertisement");
|
||||
}
|
||||
|
||||
await gattmgr.RegisterApplication("/eu/atoy", {});
|
||||
console.log("BLE GATT application registered");
|
||||
|
||||
return;
|
||||
|
||||
const bz = new Bluez();
|
||||
const pafers = await bz.getDevice("PAFERS_53B9A3");
|
||||
@@ -101,9 +53,9 @@ async function main() {
|
||||
const iscon = await pafers.isConnected();
|
||||
console.log("connected: " + iscon);
|
||||
|
||||
bz.on("interfacesAdded", () => {
|
||||
/*bz.on("interfacesAdded", () => {
|
||||
|
||||
});
|
||||
});*/
|
||||
|
||||
if (!iscon) {
|
||||
await pafers.connect();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user