-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
268 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
const unindent = require('../lib/unindent'); | ||
|
||
module.exports = ({ commandProcessor, root }) => { | ||
const esim = commandProcessor.createCategory(root, 'esim', 'Download eSIM profiles (INTERNAL ONLY)'); | ||
|
||
commandProcessor.createCommand(esim, 'provision', 'Provisions eSIM profiles on a device', { | ||
options: Object.assign({ | ||
'lpa': { | ||
description: 'Provide the LPA tool path' | ||
}, | ||
'input': { | ||
description: 'Provide the input json file path' | ||
}, | ||
'output': { | ||
description: 'Provide the output json file path' | ||
}, | ||
'bulk': { | ||
description: 'Provision multiple devices' | ||
} | ||
}), | ||
handler: (args) => { | ||
const eSimCommands = require('../cmd/esim'); | ||
if (args.bulk) { | ||
return new eSimCommands().bulkProvision(args); | ||
} else { | ||
return new eSimCommands().provision(args); | ||
} | ||
}, | ||
examples: { | ||
'$0 $command': 'TBD' | ||
}, | ||
epilogue: unindent(` | ||
The JSON file should look like this: | ||
{ | ||
"TBD": "TBD" | ||
} | ||
TBD TBD | ||
`) | ||
}); | ||
return esim; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
const spinnerMixin = require('../lib/spinner-mixin'); | ||
const usbUtils = require('../cmd/usb-util'); | ||
const fs = require('fs-extra'); | ||
const utilities = require('../lib/utilities'); | ||
const os = require('os'); | ||
const { platformForId } = require('../lib/platform'); | ||
const CLICommandBase = require('./base'); | ||
const execa = require('execa'); | ||
const SerialCommand = require('./serial'); | ||
const FlashCommand = require('./flash'); | ||
const path = require('path'); | ||
|
||
// TODO: Get these from exports | ||
const PATH_TO_PASS_THROUGH_BINARIES = '/Users/keerthyamisagadda/code/kigen-resources/binaries'; | ||
|
||
|
||
module.exports = class eSimCommands extends CLICommandBase { | ||
constructor() { // TODO: Bring ui class | ||
super(); | ||
spinnerMixin(this); | ||
this.serial = new SerialCommand(); | ||
this.lpa = null; | ||
this.inputJson = null; | ||
this.outputJson = null; | ||
} | ||
|
||
async provision(args) { | ||
this._validateArgs(args); | ||
|
||
const port = await this._getSerialPortForSingleDevice(); | ||
|
||
// get the device usb handle from the serial port | ||
const device = await this.serial.whatSerialPortDidYouMean(port); | ||
const platform = platformForId(device.specs.productId).name; | ||
|
||
console.log(`${os.EOL}Provisioning device ${device.deviceId} with platform ${platform}`); | ||
|
||
await this._flashATPassThroughFirmware(device, platform, port); | ||
|
||
// This assumes that the input JSON does not have a mapping between EID and profiles | ||
// So obtain the EID from the device and put it in outupt JSON | ||
const eid = await this._getEid(port); | ||
console.log(`${os.EOL}EID: ${eid}`); | ||
|
||
await this._checkForExistingProfiles(port); | ||
|
||
// Parse the JSON to get EID and profiles | ||
const input = fs.readFileSync(inputJson); | ||
const profilesJson = JSON.parse(input); | ||
|
||
// Work only with one device | ||
const first = profilesJson.EIDs[0]; | ||
const profiles = first.profiles; | ||
if (profiles.length === 0) { | ||
throw new Error('No profiles to provision in the input json'); | ||
} | ||
|
||
console.log(`${os.EOL}Provisioning the following profiles: `); | ||
let cnt = 0; | ||
for (const profile of profiles) { | ||
cnt++; | ||
const smdp = profile.smdp; | ||
const matchingId = profile.matching_id; | ||
const rspUrl = `1\$${smdp}\$${matchingId}`; | ||
console.log(`\t${cnt}. ${profile.provider} (${rspUrl})`); | ||
} | ||
|
||
// Download each profile | ||
for (const profile of profiles) { | ||
let iccid = null; | ||
const smdp = profile.smdp; | ||
const matchingId = profile.matching_id; | ||
const rspUrl = `1\$${smdp}\$${matchingId}`; | ||
console.log(`${os.EOL}Downloading ${profile.provider} profile from ${rspUrl}`); | ||
|
||
const start = Date.now(); | ||
|
||
const res = await execa(this.lpa, ['download', rspUrl, `--serial=${port}`]); | ||
|
||
const end = Date.now(); | ||
const timeTaken = (end - start) / 1000; | ||
|
||
const output = res.stdout; | ||
if (output.includes('Profile successfully downloaded')) { | ||
console.log(`${os.EOL}Profile successfully downloaded in ${timeTaken} sec`); | ||
const iccidLine = output.split('\n').find((line) => line.includes('Profile with ICCID')); | ||
// FIXME: Get the string after the "ICCID" string | ||
iccid = iccidLine.split(' ')[4]; | ||
} else { | ||
console.log(`${os.EOL}Profile download failed`); | ||
} | ||
|
||
const outputData = { | ||
EID: eid, | ||
provider: profile.provider, | ||
iccid: iccid, | ||
time: timeTaken, | ||
output: output | ||
}; | ||
|
||
await fs.writeJson(outputJson, outputLogs); | ||
} | ||
} | ||
|
||
_validateArgs(args) { | ||
if (!args) { | ||
throw new Error('Missing args'); | ||
} | ||
if (!args.input) { | ||
throw new Error('Missing input json file'); | ||
} | ||
if (!args.output) { | ||
throw new Error('Missing input output json file'); | ||
} | ||
if (!args.lpa) { | ||
throw new Error('Missing input LPA tool path'); | ||
} | ||
this.inputJson = args.input; | ||
this.outputJson = args.output; | ||
this.lpa = args.lpa; | ||
} | ||
|
||
async _getSerialPortForSingleDevice() { | ||
const deviceSerialPorts = await usbUtils.getUsbSystemPathsForMac(); | ||
if (deviceSerialPorts.length > 1) { | ||
throw new Error('Multiple devices found. Please unplug all but one device or use --bulk option'); | ||
} | ||
return deviceSerialPorts[0]; | ||
} | ||
|
||
async _flashATPassThroughFirmware(device, platform, port) { | ||
// Obtain the pre-compiled firmware binary from local folder | ||
// TODO: Do this with binary inspect for more reliability | ||
const fwBinaries = fs.readdirSync(PATH_TO_PASS_THROUGH_BINARIES); | ||
const validBin = fwBinaries.filter((file) => file.endsWith(`${platform}.bin`)); | ||
const fwPath = path.join(PATH_TO_PASS_THROUGH_BINARIES, validBin[0]); | ||
|
||
// Flash the AT passthrough firmware | ||
console.log(`${os.EOL}Flashing AT passthrough firmware ${fwPath} to the device`); | ||
const flashCmdInstance = new FlashCommand(); | ||
await flashCmdInstance.flashLocal({ files: [fwPath], applicationOnly: true, verbose: true }); | ||
console.log(`${os.EOL}AT passthrough firmware flashed`); | ||
|
||
const deviceResponded = await usbUtils.waitForDeviceToRespond(device.deviceId); | ||
if (!deviceResponded) { | ||
throw new Error('Device did not respond after flashing AT passthrough firmware'); | ||
} | ||
console.log(`${os.EOL}Device responded after flashing AT passthrough firmware`); | ||
await deviceResponded.close(); | ||
|
||
// FIXME: Use the firmware that does not have initial logs and then remove this block | ||
console.log(`${os.EOL}Wait for the initial logs to clear up`); | ||
const monitor = await this.serial.monitorPort({ port, follow: false }); | ||
await utilities.delay(30000); | ||
await monitor.stop(); | ||
await utilities.delay(5000); | ||
console.log(`${os.EOL}Initial logs likely cleared`); | ||
} | ||
|
||
async _getEid(port) { | ||
console.log(`${os.EOL}Getting EID from the device`); | ||
const resEid = await execa(this.lpa, ['getEid', `--serial=${port}`]); | ||
const eidOutput = resEid.stdout; | ||
let eid = null; | ||
|
||
// get the line in eidOutput that starts with "EID: " and then get the next word | ||
eidOutput.split('\n').forEach((line) => { | ||
if (line.startsWith('EID: ')) { | ||
eid = line.split(' ')[1]; | ||
} | ||
}); | ||
if (!eid) { | ||
throw new Error('EID not found in the output'); | ||
} | ||
return eid; | ||
} | ||
|
||
async _checkForExistingProfiles(port) { | ||
console.log(`${os.EOL}Checking for existing profiles`); | ||
const resProfiles = await execa(this.lpa, ['listProfiles', `--serial=${port}`]); | ||
const profilesOutput = resProfiles.stdout; | ||
const profilesList = profilesOutput | ||
.split('\n') | ||
.filter((line) => line.match(/^\d+:\[\w+,\s(?:enabled|disabled),\s?\]$/)); | ||
// console.log('Profiles list: ', profilesList); | ||
if (profilesList.length > 0) { | ||
throw new Error('Profile(s) already exist. Bad device bucket.'); | ||
} | ||
console.log(`${os.EOL}No existing profiles found`); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters