OpenHPS/openhps-rf

View on GitHub
src/data/BLEEddystoneUID.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { LengthUnit, SerializableMember, SerializableObject, UUID } from '@openhps/core';
import { concatBuffer, toHexString } from '../utils/BufferUtils';
import { BLEEddystone } from './BLEEddystone';
import { BLEUUID } from './BLEUUID';
import { BLEBeaconBuilder } from './BLEBeaconObject';
import { BLEService } from './BLEService';

@SerializableObject()
export class BLEEddystoneUID extends BLEEddystone {
    @SerializableMember()
    namespaceId: BLEUUID;

    @SerializableMember()
    instanceId: BLEUUID;

    isValid(): boolean {
        return (
            super.isValid() && this.frame === 0x00 && this.namespaceId !== undefined && this.instanceId !== undefined
        );
    }

    computeUID(): string {
        let uid = new TextDecoder().decode(this.namespaceId.toBuffer());
        uid = toHexString(concatBuffer(this.namespaceId.toBuffer(), this.instanceId.toBuffer()));
        return uid;
    }

    parseServiceData(uuid: BLEUUID, serviceData: Uint8Array): this {
        super.parseServiceData(uuid, serviceData);
        if (uuid === undefined && serviceData === undefined) {
            return this;
        }

        if (!this.service) {
            return this;
        }

        if (this.frame !== 0x00) {
            return this; // Do not attempt to parse
        }

        this.namespaceId = BLEUUID.fromBuffer(serviceData.slice(2, 12));
        this.instanceId = BLEUUID.fromBuffer(serviceData.slice(12, 18));
        if (this.uid === undefined) {
            this.uid = this.computeUID();
        }
        return this;
    }
}

/**
 * BLE Eddystone UID builder
 */
export class BLEEddystoneUIDBuilder extends BLEBeaconBuilder<BLEEddystoneUID> {
    protected constructor() {
        super();
        this.beacon = new BLEEddystoneUID();
        this.beacon.frame = 0x00;
        this.beacon.namespaceId = BLEUUID.fromString(
            UUID.generate()
                .toString()
                .replace('-', '')
                .slice(0, 10 * 2 + 1),
        );
        this.beacon.instanceId = BLEUUID.fromString(
            UUID.generate()
                .toString()
                .replace('-', '')
                .slice(0, 6 * 2 + 1),
        );
    }

    static create(): BLEEddystoneUIDBuilder {
        return new BLEEddystoneUIDBuilder();
    }

    static fromBeacon(beacon: BLEEddystoneUID): BLEEddystoneUIDBuilder {
        const builder = new BLEEddystoneUIDBuilder();
        builder.beacon = beacon;
        return builder;
    }

    namespaceId(namespaceId: BLEUUID): this {
        this.beacon.namespaceId = namespaceId;
        return this;
    }

    instanceId(instanceId: string): this {
        this.beacon.instanceId = BLEUUID.fromString(instanceId);
        return this;
    }

    calibratedRSSI(rssi: number): this {
        this.beacon.calibratedRSSI = rssi;
        return this;
    }

    build(): Promise<BLEEddystoneUID> {
        return new Promise((resolve) => {
            // Eddystone Service
            const serviceData = new DataView(new ArrayBuffer(18), 0);
            serviceData.setUint8(0, 0x00); // Eddystone-UID frame
            serviceData.setInt8(1, this.beacon.getCalibratedRSSI(0, LengthUnit.METER));
            // Namespace ID
            const namespaceId = new DataView(this.beacon.namespaceId.toBuffer().buffer, 0);
            for (let i = 2; i < 2 + 10; i++) {
                serviceData.setUint8(i, namespaceId.getUint8(i - 2));
            }
            const instanceId = new DataView(this.beacon.instanceId.toBuffer().buffer, 0);
            for (let i = 12; i < 12 + 6; i++) {
                serviceData.setUint8(i, instanceId.getUint8(i - 12));
            }

            this.beacon.addService(new BLEService(BLEUUID.fromString('FEAA'), new Uint8Array(serviceData.buffer)));
            this.beacon.uid = this.beacon.computeUID();
            resolve(this.beacon);
        });
    }
}