import Transport from "@ledgerhq/hw-transport"; import { BatteryStatusFlags, ChargingModes } from "@ledgerhq/types-devices"; import { TransportStatusError, StatusCodes } from "@ledgerhq/errors"; import { LocalTracer } from "@ledgerhq/logs"; import { LOG_TYPE } from "."; export enum BatteryStatusTypes { BATTERY_PERCENTAGE = 0x00, BATTERY_VOLTAGE = 0x01, BATTERY_TEMPERATURE = 0x02, BATTERY_CURRENT = 0x03, BATTERY_FLAGS = 0x04, } // Nb USB and BLE masks not used by implementation since we have // them from the model. export enum FlagMasks { CHARGING = 0x00000001, USB = 0x00000002, USB_POWERED = 0x00000008, BLE = 0x00000004, ISSUE_BATTERY = 0x00000080, ISSUE_CHARGING = 0x00000010, ISSUE_TEMPERATURE = 0x00000020, } // conditional type used to infer what will be the tuple of returned values of the getBatteryStatus // command. According to the tuple that of BatteryStatusTypes that is requested, it will infer the corresponding // tupple of either number or BatteryStatusFlags for the result type BatteryStatusTuple> = { [index in keyof Statuses]: Statuses[index] extends BatteryStatusTypes.BATTERY_FLAGS ? BatteryStatusFlags : number; }; const getBatteryStatus = async >( transport: Transport, statuses: StatusesType, ): Promise> => { const tracer = new LocalTracer(LOG_TYPE, { function: "getBatteryStatus", statuses, }); tracer.trace(`Starting`); const result: (BatteryStatusFlags | number)[] = []; for (let i = 0; i < statuses.length; i++) { const res = await transport.send(0xe0, 0x10, 0x00, statuses[i]); const status = res.readUInt16BE(res.length - 2); if (status !== StatusCodes.OK) { tracer.trace(`Error: status ${status}`, { status, res }); throw new TransportStatusError(status); } switch (statuses[i]) { case BatteryStatusTypes.BATTERY_PERCENTAGE: { // Nb values greater that 100 would mean a bad case // to be assessed if we want to break the flow. const temp = res.readUInt8(0); result[i] = temp > 100 ? -1 : temp; break; } case BatteryStatusTypes.BATTERY_VOLTAGE: result[i] = res.readUInt16BE(0); break; case BatteryStatusTypes.BATTERY_TEMPERATURE: case BatteryStatusTypes.BATTERY_CURRENT: // Nb turn the usigned byte into a signed int to cover // negative values. Two's compliment. result[i] = (res.readUInt8() << 24) >> 24; break; case BatteryStatusTypes.BATTERY_FLAGS: { const flags = res.readUInt16BE(2); // Nb Ignoring the first two bytes const chargingUSB = !!(flags & FlagMasks.USB_POWERED); const chargingQi = !chargingUSB && !!(flags & FlagMasks.CHARGING); result[i] = { charging: chargingQi ? ChargingModes.QI : chargingUSB ? ChargingModes.USB : ChargingModes.NONE, issueCharging: !!(flags & FlagMasks.ISSUE_CHARGING), issueTemperature: !!(flags & FlagMasks.ISSUE_TEMPERATURE), issueBattery: !!(flags & FlagMasks.ISSUE_BATTERY), }; break; } } } return result as BatteryStatusTuple; }; export default getBatteryStatus;