commit
e49f60a3d2
9 changed files with 5363 additions and 0 deletions
@ -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" |
||||
] |
||||
} |
||||
|
||||
@ -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" |
||||
} |
||||
} |
||||
@ -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"); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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", []), |
||||
}; |
||||
} |
||||
} |
||||
@ -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 |
||||
*/ |
||||
@ -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…
Reference in new issue