Browse Source

initial commit

master
Anthony Hinsinger 6 days ago
commit
e49f60a3d2
  1. 2
      .gitignore
  2. 8
      babel.config.json
  3. 5078
      package-lock.json
  4. 29
      package.json
  5. 21
      src/adv.js
  6. 9
      src/base.js
  7. 92
      src/bluez-gatt.js
  8. 103
      src/index.js
  9. 21
      webpack.config.js

2
.gitignore vendored

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
dist
node_modules

8
babel.config.json

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
{
"plugins": [
["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true, "legacy": false }],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-syntax-bigint"
]
}

5078
package-lock.json generated

File diff suppressed because it is too large Load Diff

29
package.json

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
{
"name": "bluezjs",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "babel index.js -d dist",
"start": "npm run build && node dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@babel/cli": "^7.28.3",
"@babel/core": "^7.28.5",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.28.0",
"@babel/plugin-syntax-bigint": "^7.8.3",
"@babel/preset-env": "^7.28.5",
"babel-loader": "^10.0.0",
"webpack": "^5.103.0",
"webpack-cli": "^6.0.1",
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
"dbus-next": "^0.10.2",
"typescript": "^5.9.3"
}
}

21
src/adv.js

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
import { BaseInterface } from "./base";
import dbus from "dbus-next";
export class Advertisement extends BaseInterface {
@dbus.interface.property({ signature: "s" })
Type = "peripheral";
@dbus.interface.property({ signature: "s" })
LocalName = "TestDevice";
@dbus.interface.property({ signature: "as" })
ServiceUUIDs = ["139fc001-a4ed-11ed-b9df-0242ac120003"];
//ServiceUUIDs = ["1816", "1818", "1826"];
@dbus.interface.method({ inSignature: "", outSignature: "" })
Release() {}
constructor(bus, path) {
super(bus, path, "org.bluez.LEAdvertisement1");
}
}

9
src/base.js

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
import dbus from "dbus-next";
export class BaseInterface extends dbus.interface.Interface {
constructor(bus, path, name) {
super(name);
this.path = path;
bus.export(path, this);
}
}

92
src/bluez-gatt.js

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
import { BaseInterface } from "./base";
import { Variant, interface as iface } from "dbus-next";
const { method, property } = iface;
export class BaseService extends BaseInterface {
@property({ signature: "s" })
UUID;
@property({ signature: "b" })
Primary;
@property({ signature: "ao" })
get Characteristics() {
return this.characteristics.map((c) => c.path);
}
characteristics = [];
constructor(bus, path) {
super(bus, path, "org.bluez.GattService1");
}
addCharacteristic(char) {
this.characteristics.push(char);
}
getProperties() {
return {
UUID: new Variant("s", this.UUID),
Primary: new Variant("b", this.Primary),
Characteristics: new Variant("ao", this.Characteristics),
};
}
}
export class BaseCharacteritic extends BaseInterface {
@property({ signature: "o" })
Service;
@property({ signature: "s" })
UUID;
@property({ signature: "as" })
Flags;
@property({ signature: "b" })
Notifying = false;
constructor(bus, path, service, uuid, flags) {
super(bus, path, "org.bluez.GattCharacteristic1");
this.UUID = uuid;
this.Flags = flags;
this.Service = service.path;
}
@method({ inSignature: "a{sv}", outSignature: "ay" })
ReadValue(options) {
if (this.read) {
return this.read(options);
} else {
console.log("ReadValue not definied");
}
}
@method({ inSignature: "aya{sv}" })
WriteValue(value, options) {
if (this.write) {
this.write(value, options);
} else {
console.log("WriteValue not definied");
}
}
@method({})
StartNotify() {
console.log("not definied");
}
@method({})
StopNotify() {
console.log("not definied");
}
getProperties() {
return {
Service: new Variant("o", this.Service),
UUID: new Variant("s", this.UUID),
Flags: new Variant("as", this.Flags),
Value: new Variant("ay", []),
};
}
}

103
src/index.js

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
import { systemBus, Variant, interface as iface } from "dbus-next";
const { Interface, method, property } = iface;
import { BaseService, BaseCharacteritic } from "./bluez-gatt";
import { Advertisement } from "./adv";
import { BaseInterface } from "./base";
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 = [];
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);
}
}
async function main() {
const bus = systemBus();
const app = new Application(bus, "/eu/atoy");
const adv = new Advertisement(bus, "/eu/atoy/advertising");
await bus.requestName("eu.atoy");
const obj = await bus.getProxyObject("org.bluez", "/org/bluez/hci0");
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");
}
main();
/*
Cycling Power Control Point 0x2A66 write/indicate
Cycling Power Feature 0x2A65 read 0x8a060100
Cycling Power Measurement 0x2A63 notif
Cycling Power Vector 0x2A64 notif
2a5d 0x00
*/

21
webpack.config.js

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
const path = require("path");
const nodeExternals = require("webpack-node-externals");
module.exports = {
entry: "./src/index.js",
mode: "production",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: "babel-loader",
},
],
},
target: "node",
externals: [nodeExternals()],
};
Loading…
Cancel
Save