@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
100 lines (86 loc) • 3.38 kB
text/typescript
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<Statuses extends ReadonlyArray<BatteryStatusTypes>> = {
[index in keyof Statuses]: Statuses[index] extends BatteryStatusTypes.BATTERY_FLAGS
? BatteryStatusFlags
: number;
};
const getBatteryStatus = async <StatusesType extends ReadonlyArray<BatteryStatusTypes>>(
transport: Transport,
statuses: StatusesType,
): Promise<BatteryStatusTuple<StatusesType>> => {
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<StatusesType>;
};
export default getBatteryStatus;