dvbble
Version:
Bluetooth Low Energy library for DVBuddy devices
1,083 lines • 53.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bluetooth_le_1 = require("@capacitor-community/bluetooth-le");
const core_1 = require("@capacitor/core");
const cbor_1 = __importDefault(require("cbor"));
class DVBDeviceBLE {
constructor() {
this.isConnected = false;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.userRequestedDisconnect = false;
this.SERVICE_UUID = "8d53dc1d-1db7-4cd3-868b-8a527460aa84";
this.CHARACTERISTIC_UUID = "da2e7828-fbce-4e01-ae9e-261174997c48";
this.mtu = 140;
this.device = null;
this.service = null;
this.characteristic = null;
this.connectCallback = null;
this.connectingCallback = null;
this.disconnectCallback = null;
this.messageCallback = null;
this.imageUploadProgressCallback = null;
this.imageUploadFinishedCallback = null;
this.buffer = new Uint8Array();
this.logger = { info: console.log, error: console.error };
this.seq = 0;
this.uploadImage = null;
this.uploadOffset = 0;
this.uploadSlot = 0;
this.uploadIsInProgress = false;
this.duSerialNumber = null;
this.isRegistered = false;
// DVB
this.serviceDVB = null;
this.serviceInfo = null;
this.listOfFiles = [];
this.shortname = null;
this.serialNumber = null;
this.firmwareVersion = null;
this.hardwareVersion = null;
this.duDeviceUIDVersion = null;
// Serials
this.DEVICE_INFORMATION_SERVICE_UUID = "0000180a-0000-1000-8000-00805f9b34fb";
this.SERIAL_NUMBER_UUID = "dbd00001-ff30-40a5-9ceb-a17358d31999";
this.FIRMWARE_REVISION_UUID = "00002a26-0000-1000-8000-00805f9b34fb";
this.HARDWARE_REVISION_UUID = "00002a27-0000-1000-8000-00805f9b34fb";
this.DVB_SERVICE_UUID = "dbd00001-ff30-40a5-9ceb-a17358d31999";
this.LIST_FILES_UUID = "dbd00010-ff30-40a5-9ceb-a17358d31999";
this.WRITE_TO_DEVICE_UUID = "dbd00011-ff30-40a5-9ceb-a17358d31999";
this.READ_FROM_DEVICE_UUID = "dbd00012-ff30-40a5-9ceb-a17358d31999";
this.FORMAT_STORAGE_UUID = "dbd00013-ff30-40a5-9ceb-a17358d31999";
this.SHORTNAME_UUID = "dbd00002-ff30-40a5-9ceb-a17358d31999";
this.DU_SHORTNAME_UUID = "dbd00002-ff30-40a5-9ceb-a17358d31999";
this.DU_DEVICE_UID_UUID = "dbd00003-ff30-40a5-9ceb-a17358d31999";
this.DU_SERIAL_NUMBER_UUID = "dbd00001-ff30-40a5-9ceb-a17358d31999";
this.DU_SERVER_REGISTRATION_UUID = "dbd00006-ff30-40a5-9ceb-a17358d31999";
this.DU_MANUFACTURER_SERIAL_UUID = "dbd00008-ff30-40a5-9ceb-a17358d31999";
this.DU_SENSOR_SETTING_UUID = "dbd00007-ff30-40a5-9ceb-a17358d31999";
this.manufacturerSerialNumber = null;
}
async requestBrowserDevice() {
const params = {
acceptAllDevices: false,
optionalServices: [
this.SERVICE_UUID,
this.DVB_SERVICE_UUID,
this.DEVICE_INFORMATION_SERVICE_UUID,
],
filters: [{ namePrefix: "DVB" }],
};
return navigator.bluetooth.requestDevice(params);
}
async requestMobileDevice() {
const params = {
services: [
this.SERVICE_UUID,
this.DVB_SERVICE_UUID,
this.DEVICE_INFORMATION_SERVICE_UUID,
],
allowDuplicates: false,
name: "",
};
return new Promise((resolve, reject) => {
bluetooth_le_1.BleClient.requestLEScan(params, (result) => {
if (result.localName) {
bluetooth_le_1.BleClient.stopLEScan();
resolve({
deviceId: result.device.deviceId,
name: result.localName,
});
}
}).catch(reject);
setTimeout(() => {
bluetooth_le_1.BleClient.stopLEScan();
reject(new Error("Scan timeout"));
}, 10000);
});
}
isBleDevice(device) {
return "deviceId" in device;
}
isBluetoothDevice(device) {
return "gatt" in device;
}
getDeviceDisplayName(device) {
if (!device)
return "Unknown Device";
return this.isBleDevice(device)
? device.name || device.deviceId
: device.name || "Unknown Device";
}
async connect() {
var _a;
if (core_1.Capacitor.isNativePlatform()) {
try {
this.device = await this.requestMobileDevice();
this.logger.info(`Connecting to device ${this.getDeviceDisplayName(this.device)}...`);
await bluetooth_le_1.BleClient.connect(this.device.deviceId);
this.logger.info(`Connected to device ${this.getDeviceDisplayName(this.device)}`);
// Wait for services to be discovered
await new Promise(resolve => setTimeout(resolve, 1000));
await bluetooth_le_1.BleClient.startNotifications(this.device.deviceId, this.SERVICE_UUID, this.CHARACTERISTIC_UUID, (value) => {
this.notification({ target: { value } });
});
this.isConnected = true;
}
catch (error) {
this.logger.error(`Connection error: ${error instanceof Error ? error.message : String(error)}`);
this.isConnected = false;
await this.disconnected();
throw error;
}
}
else {
try {
this.device = (await this.requestBrowserDevice());
if (!this.device) {
throw new Error("Failed to get device");
}
this.device.addEventListener("gattserverdisconnected", this.handleDisconnect.bind(this));
this.logger.info(`Connecting to device ${this.getDeviceDisplayName(this.device)}...`);
const server = await ((_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.connect());
if (!server) {
throw new Error("Failed to connect to GATT server");
}
this.logger.info("Server connected.");
// Get all required services with retries
const getServiceWithRetry = async (uuid, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
const service = await server.getPrimaryService(uuid);
if (service)
return service;
//eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (error) {
this.logger.info(`Retry ${i + 1} getting service ${uuid}`);
await new Promise(resolve => setTimeout(resolve, 500));
}
}
throw new Error(`Failed to get service ${uuid} after ${retries} retries`);
};
// Get all services in parallel
const [serviceResult, serviceDVBResult, serviceInfoResult] = await Promise.all([
getServiceWithRetry(this.SERVICE_UUID),
getServiceWithRetry(this.DVB_SERVICE_UUID),
getServiceWithRetry(this.DEVICE_INFORMATION_SERVICE_UUID)
]);
this.service = serviceResult;
this.serviceDVB = serviceDVBResult;
this.serviceInfo = serviceInfoResult;
if (!this.serviceDVB) {
throw new Error("DVB service not found");
}
// Get characteristic with retries
const getCharacteristicWithRetry = async (service, uuid, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
const characteristic = await service.getCharacteristic(uuid);
if (characteristic)
return characteristic;
//eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (error) {
this.logger.info(`Retry ${i + 1} getting characteristic ${uuid}`);
await new Promise(resolve => setTimeout(resolve, 500));
}
}
throw new Error(`Failed to get characteristic ${uuid} after ${retries} retries`);
};
// Get main characteristic
const characteristicResult = await getCharacteristicWithRetry(this.service, this.CHARACTERISTIC_UUID);
this.characteristic = characteristicResult;
if (this.characteristic) {
this.characteristic.addEventListener("characteristicvaluechanged", this.notification.bind(this));
await this.characteristic.startNotifications();
}
// Wait a bit to ensure everything is ready
await new Promise(resolve => setTimeout(resolve, 1000));
this.isConnected = true;
}
catch (error) {
this.logger.error(`Connection error: ${error instanceof Error ? error.message : String(error)}`);
this.isConnected = false;
await this.disconnected();
throw error;
}
}
if (this.connectingCallback)
this.connectingCallback();
this.logger.info("Service connected.");
this.isConnected = true;
await this.connected();
if (this.uploadIsInProgress) {
this.uploadNext();
}
}
getDeviceName() {
var _a;
return (_a = this.device) === null || _a === void 0 ? void 0 : _a.name;
}
async setDeviceInfo() {
if (!this.isConnected) {
this.logger.error("Device is not connected. Cannot set device info.");
return;
}
try {
await this.setFileList();
await this.setShortName();
await this.setSerialNumber();
await this.setHardwareVersion();
await this.setFirmwareVersion();
await this.setDUDeviceUID();
}
catch (error) {
this.logger.error(`Error setting device info: ${error instanceof Error ? error.message : String(error)}`);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async handleDisconnect(event) {
this.logger.info("Device disconnected", event);
this.isConnected = false;
if (!this.userRequestedDisconnect) {
this.logger.info("Attempting to reconnect...");
this.reconnectAttempts = 0;
this.reconnect();
}
else {
console.log("User requested disconnect");
await this.disconnected();
}
}
async reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
this.logger.error("Max reconnection attempts reached. Please try connecting manually.");
await this.disconnected();
return;
}
this.reconnectAttempts++;
this.logger.info(`Reconnection attempt ${this.reconnectAttempts}...`);
try {
await this.connect();
}
catch (error) {
this.logger.error(`Reconnection error: ${error instanceof Error ? error.message : String(error)}`);
setTimeout(() => this.reconnect(), 2000);
}
}
disconnect() {
var _a;
this.userRequestedDisconnect = true;
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
return Promise.resolve();
}
return bluetooth_le_1.BleClient.disconnect(this.device.deviceId);
}
else {
if (!this.device || !this.isBluetoothDevice(this.device)) {
return Promise.resolve();
}
return (_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.disconnect();
}
}
onConnecting(callback) {
this.connectingCallback = callback;
return this;
}
onConnect(callback) {
this.connectCallback = callback;
return this;
}
onDisconnect(callback) {
this.disconnectCallback = callback;
return this;
}
onMessage(callback) {
this.messageCallback = callback;
return this;
}
onImageUploadProgress(callback) {
this.imageUploadProgressCallback = callback;
return this;
}
onImageUploadFinished(callback) {
this.imageUploadFinishedCallback = callback;
return this;
}
async connected() {
this.userRequestedDisconnect = false;
if (this.connectCallback)
this.connectCallback();
}
async disconnected() {
this.logger.info("Disconnected.");
if (this.disconnectCallback)
this.disconnectCallback();
this.device = null;
this.service = null;
this.serviceDVB = null;
this.serviceInfo = null;
this.characteristic = null;
this.uploadIsInProgress = false;
this.serialNumber = null;
this.listOfFiles = [];
}
async sendMessage(op, group, id, data) {
const _flags = 0;
let encodedData = [];
if (typeof data !== "undefined") {
encodedData = [...new Uint8Array(cbor_1.default.encode(data))];
}
const length_lo = encodedData.length & 255;
const length_hi = encodedData.length >> 8;
const group_lo = group & 255;
const group_hi = group >> 8;
const message = [
op,
_flags,
length_hi,
length_lo,
group_hi,
group_lo,
this.seq,
id,
...encodedData,
];
this.logger.info(`Sending message: op=${op}, group=${group}, id=${id}, length=${encodedData.length}`);
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
const messageArray = Uint8Array.from(message);
await bluetooth_le_1.BleClient.writeWithoutResponse(this.device.deviceId, this.SERVICE_UUID, this.CHARACTERISTIC_UUID, new DataView(messageArray.buffer));
}
else {
if (!this.characteristic) {
throw new Error("Characteristic not available");
}
await this.characteristic.writeValueWithoutResponse(Uint8Array.from(message));
}
this.seq = (this.seq + 1) % 256;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
notification(event) {
console.log("message received");
const message = new Uint8Array(event.target.value.buffer);
this.buffer = new Uint8Array([...this.buffer, ...message]);
const messageLength = this.buffer[2] * 256 + this.buffer[3];
if (this.buffer.length < messageLength + 8)
return;
this.processMessage(this.buffer.slice(0, messageLength + 8));
this.buffer = this.buffer.slice(messageLength + 8);
}
processMessage(message) {
const [op, _flags, length_hi, length_lo, group_hi, group_lo, _seq, id] = message;
void _flags;
void _seq;
const data = cbor_1.default.decode(message.slice(8).buffer);
const length = length_hi * 256 + length_lo;
const group = group_hi * 256 + group_lo;
this.logger.info(`Processing message - op: ${op}, group: ${group}, id: ${id}, data:`, data);
if (group === 1 && id === 1) {
if (data.rc === 0 || data.rc === undefined) {
if (data.off !== undefined) {
this.uploadOffset = data.off;
this.logger.info(`Upload offset updated to: ${this.uploadOffset}`);
this.uploadNext();
}
}
else {
this.logger.error(`Upload error received: rc=${data.rc}`);
this.uploadIsInProgress = false;
if (this.imageUploadFinishedCallback) {
this.imageUploadFinishedCallback();
}
}
return;
}
if (this.messageCallback)
this.messageCallback({ op, group, id, data, length });
}
cmdReset() {
return this.sendMessage(2, 0, 5);
}
smpEcho(message) {
return this.sendMessage(2, 0, 0, { d: message });
}
cmdImageState() {
return this.sendMessage(0, 1, 0);
}
cmdImageErase() {
return this.sendMessage(2, 1, 5, {});
}
cmdImageTest(hash) {
return this.sendMessage(2, 1, 0, {
hash,
confirm: false,
});
}
cmdImageConfirm(hash) {
return this.sendMessage(2, 1, 0, {
hash,
confirm: true,
});
}
hash(image) {
return crypto.subtle.digest("SHA-256", image);
}
async uploadNext() {
if (!this.uploadImage) {
this.logger.info("No image to upload");
this.uploadIsInProgress = false;
return;
}
if (this.uploadOffset >= this.uploadImage.byteLength) {
this.logger.info("Upload complete - reached end of image");
this.uploadIsInProgress = false;
if (this.imageUploadFinishedCallback) {
this.imageUploadFinishedCallback();
}
return;
}
const nmpOverhead = 8;
const message = { data: new Uint8Array(), off: this.uploadOffset };
if (this.uploadOffset === 0) {
this.logger.info(`Starting upload of ${this.uploadImage.byteLength} bytes`);
message.len = this.uploadImage.byteLength;
message.sha = new Uint8Array(await this.hash(this.uploadImage));
this.logger.info("Image hash:", Array.from(message.sha).map(b => b.toString(16).padStart(2, '0')).join(' '));
}
// Calculate progress percentage
const progress = Math.floor((this.uploadOffset / this.uploadImage.byteLength) * 100);
if (this.imageUploadProgressCallback) {
this.imageUploadProgressCallback({ percentage: progress });
}
const length = this.mtu - cbor_1.default.encode(message).byteLength - nmpOverhead;
message.data = new Uint8Array(this.uploadImage.slice(this.uploadOffset, this.uploadOffset + length));
this.logger.info(`Sending chunk at offset ${this.uploadOffset}, length ${message.data.length} bytes`);
this.uploadOffset += length;
try {
await this.sendMessage(2, 1, 1, message);
this.logger.info("Chunk sent successfully");
}
catch (error) {
this.logger.error("Error during upload:", error);
this.uploadIsInProgress = false;
throw error;
}
}
async cmdUpload(image, slot = 0) {
if (this.uploadIsInProgress) {
this.logger.error("Upload is already in progress.");
return;
}
this.logger.info(`Starting firmware upload to slot ${slot}`);
this.uploadIsInProgress = true;
this.uploadOffset = 0;
this.uploadImage = image;
this.uploadSlot = slot;
try {
// First, send the image upload command
this.logger.info("Sending image upload command...");
await this.sendMessage(2, 1, 2, {
slot,
size: image.byteLength,
hash: new Uint8Array(await this.hash(image))
});
// Wait a bit for the device to process
await new Promise(resolve => setTimeout(resolve, 500));
// Then start sending the actual data chunks
this.logger.info("Starting to send image data chunks...");
await this.uploadNext();
}
catch (error) {
this.logger.error("Error during upload initialization:", error);
this.uploadIsInProgress = false;
throw error;
}
}
async imageInfo(image) {
const info = { version: "", hash: new Uint8Array() };
const buffer = ArrayBuffer.isView(image) ? image.buffer : image;
const view = new Uint8Array(buffer);
if (view.length < 4096) {
throw new Error("Image header is too short");
}
const version = [view[12], view[13], view[14], view[15]].join(".");
info.version = version;
const hashStart = 20;
const hashEnd = hashStart + 32;
const hash = view.slice(hashStart, hashEnd);
info.hash = hash;
return info;
}
getShortName() {
return this.shortname;
}
async setShortName(shortname) {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
if (!shortname) {
const result = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.SHORTNAME_UUID);
this.shortname = new TextDecoder().decode(result);
}
else {
const uf8encode = new TextEncoder();
const newShortName = uf8encode.encode(shortname);
await bluetooth_le_1.BleClient.write(this.device.deviceId, this.DVB_SERVICE_UUID, this.SHORTNAME_UUID, new DataView(newShortName.buffer));
this.shortname = shortname;
}
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
if (!shortname) {
const characteristic = await this.serviceDVB.getCharacteristic(this.SHORTNAME_UUID);
const value = await characteristic.readValue();
this.shortname = new TextDecoder().decode(value);
}
else {
const characteristic = await this.serviceDVB.getCharacteristic(this.SHORTNAME_UUID);
const uf8encode = new TextEncoder();
const newShortName = uf8encode.encode(shortname);
await characteristic.writeValue(newShortName);
this.shortname = shortname;
}
}
}
catch (error) {
this.logger.error(error);
}
}
getFileList() {
return this.listOfFiles;
}
async setFileList() {
if (!this.isConnected) {
this.logger.error("Device is not connected. Cannot set file list.");
return;
}
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
while (true) {
const value = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.LIST_FILES_UUID);
const message = new Uint8Array(value.buffer);
if (message.byteLength === 0)
return;
const byteString = String.fromCharCode(...message);
const split_string = byteString.split(";");
const name = split_string[0];
const length = split_string[1];
this.listOfFiles.push({ name, length });
}
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
while (true) {
const characteristic = await this.serviceDVB.getCharacteristic(this.LIST_FILES_UUID);
const value = await characteristic.readValue();
const message = new Uint8Array(value.buffer);
if (message.byteLength === 0)
return;
const byteString = String.fromCharCode(...message);
const split_string = byteString.split(";");
const name = split_string[0];
const length = split_string[1];
this.listOfFiles.push({ name, length });
}
}
}
catch (error) {
if (error instanceof Error) {
this.logger.error(`Error setting file list: ${error.message}`);
}
else {
this.logger.error(`Error setting file list: ${error}`);
}
}
}
async getFileContent(name, progressCallback) {
try {
const arrayBuffers = [];
let offset = 0;
let totalSize = 0;
const CHUNK_SIZE = 65536;
const fileInfo = this.listOfFiles.find((file) => file.name === name);
if (fileInfo) {
totalSize = parseInt(fileInfo.length);
}
const utf8encoder = new TextEncoder();
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
while (true) {
try {
const name_bytes = utf8encoder.encode(`${name};${offset};`);
await bluetooth_le_1.BleClient.write(this.device.deviceId, this.DVB_SERVICE_UUID, this.WRITE_TO_DEVICE_UUID, new DataView(name_bytes.buffer));
const display_info = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.READ_FROM_DEVICE_UUID);
if (display_info.byteLength !== 0) {
const array = new Uint8Array(display_info.buffer);
array.map((x) => arrayBuffers.push(x));
if (arrayBuffers.length % CHUNK_SIZE === 0) {
offset += CHUNK_SIZE;
this.logger.info(`Reached 64 KB, updating offset: ${offset}`);
}
if (totalSize > 0 && progressCallback) {
const progress = Math.min(100, Math.round((arrayBuffers.length / totalSize) * 100));
progressCallback(progress);
}
}
else {
break;
}
}
catch (error) {
this.logger.error(`Error reading data, retrying at offset ${offset}`, error);
await new Promise((resolve) => setTimeout(resolve, 500)); // Small delay before retrying
}
}
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const write_characteristic = await this.serviceDVB.getCharacteristic(this.WRITE_TO_DEVICE_UUID);
const read_characteristic = await this.serviceDVB.getCharacteristic(this.READ_FROM_DEVICE_UUID);
while (true) {
try {
if (arrayBuffers.length % CHUNK_SIZE === 0) {
const name_bytes = utf8encoder.encode(`${name};${offset};`);
await write_characteristic.writeValue(name_bytes);
}
const display_info = await read_characteristic.readValue();
if (display_info.byteLength !== 0) {
const array = new Uint8Array(display_info.buffer);
arrayBuffers.push(...array);
if (arrayBuffers.length >= offset + CHUNK_SIZE) {
offset += CHUNK_SIZE;
this.logger.info(`Reached 64 KB, updating offset: ${offset}`);
}
if (totalSize > 0 && progressCallback) {
const progress = Math.min(100, Math.round((arrayBuffers.length / totalSize) * 100));
progressCallback(progress);
}
}
else {
break;
}
}
catch (error) {
this.logger.error(`Error reading data, retrying at offset ${offset}`, error);
await new Promise((resolve) => setTimeout(resolve, 500));
}
}
}
return new Uint8Array(arrayBuffers);
}
catch (error) {
this.logger.error(error);
}
}
async formatStorage() {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.FORMAT_STORAGE_UUID);
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.FORMAT_STORAGE_UUID);
await characteristic.readValue();
}
this.logger.info("Files erased");
}
catch (error) {
this.logger.error(error);
}
}
getSerialNumber() {
return this.serialNumber;
}
async setSerialNumber() {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
const serial = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DEVICE_INFORMATION_SERVICE_UUID, this.SERIAL_NUMBER_UUID);
const serialNumber = new TextDecoder().decode(serial);
this.serialNumber = serialNumber;
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.SERIAL_NUMBER_UUID);
const serial = await characteristic.readValue();
const serialNumber = new TextDecoder().decode(serial);
this.serialNumber = serialNumber;
this.logger.info(`Serial Number: ${this.serialNumber}`);
}
}
catch (error) {
this.logger.error(error);
}
}
getFirmwareVersion() {
return this.firmwareVersion;
}
async setFirmwareVersion() {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
const firmware = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DEVICE_INFORMATION_SERVICE_UUID, this.FIRMWARE_REVISION_UUID);
const firmwareVersion = new TextDecoder().decode(firmware);
this.logger.info("Firmware Version:", firmwareVersion);
this.firmwareVersion = firmwareVersion;
}
else {
if (!this.serviceInfo) {
throw new Error("Device information service not available");
}
const characteristic = await this.serviceInfo.getCharacteristic(this.FIRMWARE_REVISION_UUID);
const firmware = await characteristic.readValue();
const firmwareVersion = new TextDecoder().decode(firmware);
this.logger.info("Firmware Version:", firmwareVersion);
this.firmwareVersion = firmwareVersion;
}
}
catch (error) {
this.logger.error("Error getting firmware version:", error);
throw error;
}
}
getHardwareVersion() {
return this.hardwareVersion;
}
async setHardwareVersion() {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
const hardware = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DEVICE_INFORMATION_SERVICE_UUID, this.HARDWARE_REVISION_UUID);
const hardwareVersion = new TextDecoder().decode(hardware);
this.logger.info("Hardware Version:", hardwareVersion);
this.hardwareVersion = hardwareVersion;
}
else {
if (!this.serviceInfo) {
throw new Error("Device information service not available");
}
const characteristic = await this.serviceInfo.getCharacteristic(this.HARDWARE_REVISION_UUID);
const hardware = await characteristic.readValue();
const hardwareVersion = new TextDecoder().decode(hardware);
this.logger.info("Hardware Version:", hardwareVersion);
this.hardwareVersion = hardwareVersion;
}
}
catch (error) {
this.logger.error("Error getting firmware version:", error);
throw error;
}
}
getDUDeviceUID() {
return this.duDeviceUIDVersion;
}
async setDUDeviceUID() {
try {
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
const duDeviceUID = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_DEVICE_UID_UUID);
const duDeviceUIDString = new TextDecoder().decode(duDeviceUID);
this.logger.info("DUDeviceUID:", duDeviceUIDString);
this.duDeviceUIDVersion = duDeviceUIDString;
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_DEVICE_UID_UUID);
const duDeviceUID = await characteristic.readValue();
const duDeviceUIDVersion = new TextDecoder().decode(duDeviceUID);
this.logger.info("DU Device UID Version:", duDeviceUIDVersion);
this.duDeviceUIDVersion = duDeviceUIDVersion;
}
}
catch (error) {
this.logger.error("Error getting DUDeviceUID", error);
throw error;
}
}
getDUSerialNumber() {
return this.duSerialNumber;
}
async readDUSerialNumber() {
try {
if (!this.isConnected) {
throw new Error("Device is not connected");
}
// Add a small delay to ensure services are ready
await new Promise(resolve => setTimeout(resolve, 500));
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
this.logger.info("Reading DU Serial Number from native platform...");
const duSerialNumber = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_SERIAL_NUMBER_UUID);
const decodedValue = new TextDecoder().decode(duSerialNumber);
this.logger.info("Raw DU Serial Number value:", Array.from(new Uint8Array(duSerialNumber.buffer)).map(b => b.toString(16).padStart(2, '0')).join(' '));
this.logger.info("Decoded DU Serial Number:", decodedValue);
if (!decodedValue || decodedValue.trim() === '') {
throw new Error("Received empty DU Serial Number");
}
this.duSerialNumber = decodedValue;
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
this.logger.info("Reading DU Serial Number from web platform...");
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_SERIAL_NUMBER_UUID);
if (!characteristic) {
throw new Error("DU Serial Number characteristic not found");
}
this.logger.info("DU Serial Number characteristic properties:", {
read: characteristic.properties.read,
write: characteristic.properties.write,
writeWithoutResponse: characteristic.properties.writeWithoutResponse,
notify: characteristic.properties.notify,
indicate: characteristic.properties.indicate,
broadcast: characteristic.properties.broadcast,
authenticatedSignedWrites: characteristic.properties.authenticatedSignedWrites,
reliableWrite: characteristic.properties.reliableWrite,
writableAuxiliaries: characteristic.properties.writableAuxiliaries,
});
const duSerialNumber = await characteristic.readValue();
const decodedValue = new TextDecoder().decode(duSerialNumber);
this.logger.info("Raw DU Serial Number value:", Array.from(new Uint8Array(duSerialNumber.buffer)).map(b => b.toString(16).padStart(2, '0')).join(' '));
this.logger.info("Decoded DU Serial Number:", decodedValue);
if (!decodedValue || decodedValue.trim() === '') {
throw new Error("Received empty DU Serial Number");
}
this.duSerialNumber = decodedValue;
}
}
catch (error) {
this.logger.error("Error reading DU Serial Number:", error);
this.duSerialNumber = null;
throw error;
}
}
getManufacturerSerialNumber() {
return this.manufacturerSerialNumber;
}
async readManufacturerSerialNumber() {
try {
if (!this.isConnected) {
throw new Error("Device is not connected");
}
// Add a small delay to ensure services are ready
await new Promise(resolve => setTimeout(resolve, 500));
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
this.logger.info("Reading Manufacturer Serial Number from native platform...");
const manufacturerSerial = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_MANUFACTURER_SERIAL_UUID);
const decodedValue = new TextDecoder().decode(manufacturerSerial);
this.logger.info("Raw Manufacturer Serial Number value:", Array.from(new Uint8Array(manufacturerSerial.buffer)).map(b => b.toString(16).padStart(2, '0')).join(' '));
this.logger.info("Decoded Manufacturer Serial Number:", decodedValue);
this.manufacturerSerialNumber = decodedValue;
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
this.logger.info("Reading Manufacturer Serial Number from web platform...");
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_MANUFACTURER_SERIAL_UUID);
if (!characteristic) {
throw new Error("Manufacturer Serial Number characteristic not found");
}
// Log the characteristic properties for debugging
this.logger.info("Manufacturer Serial Number characteristic properties:", {
read: characteristic.properties.read,
write: characteristic.properties.write,
writeWithoutResponse: characteristic.properties.writeWithoutResponse,
notify: characteristic.properties.notify,
indicate: characteristic.properties.indicate,
broadcast: characteristic.properties.broadcast,
authenticatedSignedWrites: characteristic.properties.authenticatedSignedWrites,
reliableWrite: characteristic.properties.reliableWrite,
writableAuxiliaries: characteristic.properties.writableAuxiliaries,
});
const manufacturerSerial = await characteristic.readValue();
const decodedValue = new TextDecoder().decode(manufacturerSerial);
this.logger.info("Raw Manufacturer Serial Number value:", Array.from(new Uint8Array(manufacturerSerial.buffer)).map(b => b.toString(16).padStart(2, '0')).join(' '));
this.logger.info("Decoded Manufacturer Serial Number:", decodedValue);
this.manufacturerSerialNumber = decodedValue;
}
}
catch (error) {
this.logger.error("Error reading Manufacturer Serial Number:", error);
throw error;
}
}
async calculateSHA3Signature(serialNumber, randomValue) {
// Test key as specified in the documentation
const dvb_mac_key = new Uint8Array([
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
]);
// Convert serial number to bytes (96-bit = 12 bytes)
const serialBytes = new TextEncoder().encode(serialNumber);
// Combine the data as specified in the documentation
const data = new Uint8Array(34); // 16 + 12 + 4 + 2 bytes
data.set(dvb_mac_key, 0); // dvb_mac_key[0-15]
data.set(serialBytes.slice(0, 12), 16); // 96-bit Serial Number [0-11]
data.set(randomValue, 28); // Random Value [28-31]
// Calculate SHA-256 hash (using SHA-256 as SHA3 is not supported by Web Crypto API)
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = new Uint8Array(hashBuffer);
// Return first 4 bytes as signature
return hashArray.slice(0, 4);
}
async verifyDevice() {
try {
if (!this.isConnected) {
throw new Error("Device is not connected");
}
// First read the DU Serial Number
await this.readDUSerialNumber();
const serialNumber = this.getDUSerialNumber();
if (!serialNumber) {
throw new Error("Failed to read DU Serial Number");
}
this.logger.info("DU Serial Number for verification:", serialNumber);
// Generate random value (4 bytes)
const randomValue = new Uint8Array(4);
crypto.getRandomValues(randomValue);
this.logger.info("Generated random value:", Array.from(randomValue).map(b => b.toString(16).padStart(2, '0')).join(' '));
// Write random value to DU
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
await bluetooth_le_1.BleClient.write(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_SERVER_REGISTRATION_UUID, new DataView(randomValue.buffer));
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_SERVER_REGISTRATION_UUID);
if (!characteristic) {
throw new Error("Server Registration characteristic not found");
}
if (!characteristic.properties.write && !characteristic.properties.writeWithoutResponse) {
throw new Error("Characteristic does not support writing");
}
if (characteristic.properties.writeWithoutResponse) {
await characteristic.writeValueWithoutResponse(randomValue);
}
else {
await characteristic.writeValue(randomValue);
}
}
// Wait a bit for the device to process
await new Promise(resolve => setTimeout(resolve, 500));
// Read the response (should be SHA3 signature)
let response;
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
response = await bluetooth_le_1.BleClient.read(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_SERVER_REGISTRATION_UUID);
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_SERVER_REGISTRATION_UUID);
response = await characteristic.readValue();
}
// Calculate expected signature
const expectedSignature = await this.calculateSHA3Signature(serialNumber, randomValue);
// Compare signatures
const responseArray = new Uint8Array(response.buffer);
this.logger.info("Received signature length:", responseArray.length);
this.logger.info("Received signature:", Array.from(responseArray).map(b => b.toString(16).padStart(2, '0')).join(' '));
this.logger.info("Expected signature:", Array.from(expectedSignature).map(b => b.toString(16).padStart(2, '0')).join(' '));
if (responseArray.length !== 4) {
this.logger.error(`Invalid signature length: ${responseArray.length} (expected 4)`);
return false;
}
const isVerified = responseArray.every((byte, index) => byte === expectedSignature[index]);
this.logger.info("Device registration:", isVerified ? "OK" : "Error");
return isVerified;
}
catch (error) {
this.logger.error("Error during device registration", error);
throw error;
}
}
async calibrateAccel() {
try {
if (!this.isConnected) {
throw new Error("Device is not connected");
}
const command = new Uint8Array([0x10]);
if (core_1.Capacitor.isNativePlatform()) {
if (!this.device || !this.isBleDevice(this.device)) {
throw new Error("Device not connected or not a BLE device");
}
await bluetooth_le_1.BleClient.write(this.device.deviceId, this.DVB_SERVICE_UUID, this.DU_SENSOR_SETTING_UUID, new DataView(command.buffer));
}
else {
if (!this.serviceDVB) {
throw new Error("DVB service not available");
}
const characteristic = await this.serviceDVB.getCharacteristic(this.DU_SENSOR_SETTING_UUID);
if (!characteristic) {
throw new Error("Sensor Setting characteristic not found");
}
if (!characteristic.properties.write && !characteristic.properties.writeWithoutResponse) {
throw new Error("Characteristic does not support writing");
}
if (characteristic.properties.writeWithoutResponse) {
await characteristic.writeValueWithoutResponse(command);
}
else {
await characteristic.writeValue(command);
}
}
this.logger.info("ACCEL calibration command sent successfully");
}
catch (error) {
this.logger.error("Error sending ACCEL calibration command", error);
throw