Skip to content

Commit

Permalink
Add TOKENS_INFO menu. Cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec committed Jun 29, 2024
1 parent b7a592b commit 9173579
Show file tree
Hide file tree
Showing 12 changed files with 410 additions and 38 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"no-bitwise": "off",
"no-await-in-loop": "off",
"no-undef-init": "off",
"perfectionist/sort-enums": "off"
"perfectionist/sort-enums": "off",
"perfectionist/sort-objects": "off",
"perfectionist/sort-object-types": "off",
"unicorn/prefer-math-trunc": "off",
"@typescript-eslint/no-floating-promises": "error"
}
}
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ Interact with EmberZNet-based adapters using zigbee-herdsman 'ember' driver
- Backup network
- Restore network
- Leave network
- Backup tokens (NVM3)
- Restore tokens (NVM3)
- Reset tokens (NVM3)
- Get NVM3 tokens info (details of what Backup saves)
- Backup NVM3 tokens
- Restore NVM3 tokens
- Reset NVM3 tokens
- Get security info
- Repairs
- Check for EUI64 mismatch
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"cli-progress": "^3.12.0",
"crc-32": "^1.2.2",
"winston": "^3.13.0",
"zigbee-herdsman": "file:zigbee-herdsman-6fd50e5.tgz"
"zigbee-herdsman": "file:zigbee-herdsman-0e18acd.tgz"
},
"devDependencies": {
"@oclif/prettier-config": "^0.2.1",
Expand All @@ -41,7 +41,7 @@
"/bin",
"/dist",
"/oclif.manifest.json",
"/zigbee-herdsman-6fd50e5.tgz"
"/zigbee-herdsman-0e18acd.tgz"
],
"homepage": "https://github.com/Nerivec/ember-zli",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion src/commands/bootloader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export default class Bootloader extends Command {
private async selectFirmware(gecko: GeckoBootloader): Promise<Buffer> {
const firmwareSource = await select<FirmwareSource>({
choices: [
{ disabled: (gecko.adapterModel === undefined), name: 'Use pre-defined firmware (recommended or latest based on your adapter)', value: FirmwareSource.PRE_DEFINED },
{ name: 'Use pre-defined firmware (recommended or latest based on your adapter)', value: FirmwareSource.PRE_DEFINED, disabled: (gecko.adapterModel === undefined)},
{ name: 'Provide URL', value: FirmwareSource.URL },
{ name: `Select file in data folder (${DATA_FOLDER})`, value: FirmwareSource.DATA_FOLDER },
],
Expand Down
75 changes: 56 additions & 19 deletions src/commands/stack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { EmberInitialSecurityState, EmberNetworkParameters, EmberZigbeeNetwork,
import { initSecurityManagerContext } from 'zigbee-herdsman/dist/adapter/ember/utils/initters.js'
import { toUnifiedBackup } from 'zigbee-herdsman/dist/utils/backup.js'

import { DATA_FOLDER, DEFAULT_NETWORK_BACKUP_PATH, DEFAULT_STACK_CONFIG_PATH, DEFAULT_TOKENS_BACKUP_PATH, logger } from '../../index.js'
import { backupNetwork, emberFullVersion, emberNetworkInit, emberStart, emberStop, getBackup, getStackConfig, waitForStackStatus } from '../../utils/ember.js'
import { DATA_FOLDER, DEFAULT_NETWORK_BACKUP_PATH, DEFAULT_STACK_CONFIG_PATH, DEFAULT_TOKENS_BACKUP_PATH, DEFAULT_TOKENS_INFO_PATH, logger } from '../../index.js'
import { backupNetwork, emberFullVersion, emberNetworkInit, emberStart, emberStop, getBackup, getStackConfig, getTokensInfo, importLinkKeys, waitForStackStatus } from '../../utils/ember.js'
import { NVM3ObjectKey } from '../../utils/enums.js'
import { getPortConf } from '../../utils/port.js'
import { LinkKeyBackupData } from '../../utils/types.js'

enum StackMenu {
STACK_INFO = 0,
Expand All @@ -28,6 +30,7 @@ enum StackMenu {
TOKENS_BACKUP = 20,
TOKENS_RESTORE = 21,
TOKENS_RESET = 22,
TOKENS_INFO = 23,

SECURITY_INFO = 30,

Expand All @@ -41,10 +44,6 @@ enum RepairId {
const BULLET_FULL = '\u2022'
const BULLET_EMPTY = '\u2219'

// from EmberTokensManager
const NVM3KEY_DOMAIN_ZIGBEE = 0x10000
const NVM3KEY_STACK_TRUST_CENTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE124)

export default class Stack extends Command {
static override args = {
}
Expand Down Expand Up @@ -264,11 +263,6 @@ export default class Stack extends Command {
return false
}

if (backup.devices.length > 0) {
logger.error(`Restoring with App Link Keys currently not supported by CLI.`)
return false
}

const radioTxPower = Number.parseInt(await input({
default: '5',
message: 'Initial radio transmit power [0-20]',
Expand All @@ -291,7 +285,7 @@ export default class Stack extends Command {
}

if (!noNetwork) {
const overwrite = await confirm({ default: false, message: 'A network is present in the adapter. Continue restoring?' })
const overwrite = await confirm({ default: false, message: 'A network is present in the adapter. Leave and continue restoring?' })

if (!overwrite) {
logger.info(`Restore cancelled.`)
Expand All @@ -308,6 +302,21 @@ export default class Stack extends Command {
await waitForStackStatus(this, ezsp, SLStatus.NETWORK_DOWN)
}

const keyList: LinkKeyBackupData[] = backup.devices.map((device) => {
const octets = [...device.ieeeAddress.reverse()]

return {
deviceEui64: `0x${octets.map(octet => octet.toString(16).padStart(2, '0')).join('')}`,
// won't export if linkKey not present, so should always be valid here
key: {contents: device.linkKey!.key},
outgoingFrameCounter: device.linkKey!.txCounter,
incomingFrameCounter: device.linkKey!.rxCounter,
}
})

// before forming
await importLinkKeys(this, ezsp, keyList)

const state: EmberInitialSecurityState = {
bitmask: (
EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY
Expand Down Expand Up @@ -447,11 +456,15 @@ export default class Stack extends Command {
logger.debug(`ezspEnergyScanResultHandler: ${JSON.stringify({ channel, maxRssiValue })}`)
const full = 90 + maxRssiValue
const empty = 90 - full
reportedValues.push(`Channel ${channel}: ${BULLET_FULL.repeat(full)}${BULLET_EMPTY.repeat(empty)} [${maxRssiValue} dBm]`)

if (full < 1 || empty < 1) {
reportedValues.push(`Channel ${channel}: ERROR`)
} else {
reportedValues.push(`Channel ${channel}: ${BULLET_FULL.repeat(full)}${BULLET_EMPTY.repeat(empty)} [${maxRssiValue} dBm]`)
}
}

ezsp.ezspNetworkFoundHandler = (networkFound: EmberZigbeeNetwork, lastHopLqi: number, lastHopRssi: number): void => {
// eslint-disable-next-line perfectionist/sort-objects
logger.debug(`ezspNetworkFoundHandler: ${JSON.stringify({ networkFound, lastHopLqi, lastHopRssi })}`)
reportedValues.push(`Found network: PAN ID: ${networkFound.panId}, channel: ${networkFound.channel}, Node RSSI: ${lastHopRssi} dBm, LQI: ${lastHopLqi}.`)
}
Expand Down Expand Up @@ -538,7 +551,7 @@ export default class Stack extends Command {

logger.warning(`Fixing EUI64 mismatch...`)

const [gtkStatus, tokenData] = await ezsp.ezspGetTokenData(NVM3KEY_STACK_TRUST_CENTER, 0)
const [gtkStatus, tokenData] = await ezsp.ezspGetTokenData(NVM3ObjectKey.STACK_TRUST_CENTER, 0)

if (gtkStatus !== SLStatus.OK) {
logger.error(`Failed get token data request with status=${SLStatus[gtkStatus]}.`)
Expand All @@ -551,7 +564,7 @@ export default class Stack extends Command {
if (tokenEUI64.equals(tcEUI64)) {
tokenData.data.set(Buffer.from(eui64.slice(2/* 0x */), 'hex').reverse(), 2/* skip uint16_t at start */)

const stkStatus = await ezsp.ezspSetTokenData(NVM3KEY_STACK_TRUST_CENTER, 0, tokenData)
const stkStatus = await ezsp.ezspSetTokenData(NVM3ObjectKey.STACK_TRUST_CENTER, 0, tokenData)

if (stkStatus !== SLStatus.OK) {
logger.error(`Failed set token data request with status=${SLStatus[stkStatus]}.`)
Expand Down Expand Up @@ -703,6 +716,25 @@ export default class Stack extends Command {
return false
}

private async menuTokensInfo(ezsp: Ezsp): Promise<boolean> {
let saveFile: string | undefined = undefined

if (await confirm({ default: false, message: 'Save to file? (Only print if not)' })) {
saveFile = await this.browseToFile('Info save location (JSON)', DEFAULT_TOKENS_INFO_PATH)
}

const tokensInfo = await getTokensInfo(this, ezsp)

if (tokensInfo === null) {
logger.error(`Failed to get tokens info.`)
} else if (saveFile !== undefined) {
writeFileSync(saveFile, JSON.stringify(tokensInfo, null, 2), 'utf8')
logger.info(`Tokens info written to '${saveFile}'.`)
}

return false
}

private async menuTokensReset(ezsp: Ezsp): Promise<boolean> {
const confirmed = await confirm({ default: false, message: 'Confirm tokens reset? (Cannot be undone without a backup.)' })

Expand Down Expand Up @@ -748,9 +780,10 @@ export default class Stack extends Command {
{ name: 'Backup network', value: StackMenu.NETWORK_BACKUP },
{ name: 'Restore network', value: StackMenu.NETWORK_RESTORE },
{ name: 'Leave network', value: StackMenu.NETWORK_LEAVE },
{ name: 'Backup tokens (NVM3)', value: StackMenu.TOKENS_BACKUP },
{ name: 'Restore tokens (NVM3)', value: StackMenu.TOKENS_RESTORE },
{ name: 'Reset tokens (NVM3)', value: StackMenu.TOKENS_RESET },
{ name: 'Get NVM3 tokens info (details of what Backup saves)', value: StackMenu.TOKENS_INFO },
{ name: 'Backup NVM3 tokens', value: StackMenu.TOKENS_BACKUP },
{ name: 'Restore NVM3 tokens', value: StackMenu.TOKENS_RESTORE },
{ name: 'Reset NVM3 tokens', value: StackMenu.TOKENS_RESET },
{ name: 'Get security info', value: StackMenu.SECURITY_INFO },
{ name: 'Repairs', value: StackMenu.REPAIRS },
{ name: 'Exit', value: -1 },
Expand Down Expand Up @@ -787,6 +820,10 @@ export default class Stack extends Command {
return this.menuNetworkLeave(ezsp)
}

case StackMenu.TOKENS_INFO: {
return this.menuTokensInfo(ezsp)
}

case StackMenu.TOKENS_BACKUP: {
return this.menuTokensBackup(ezsp)
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const CONF_NETWORK_PATH = join(DATA_FOLDER, 'conf_network.json')
export const DEFAULT_STACK_CONFIG_PATH = join(DATA_FOLDER, 'stack_config.json')
export const DEFAULT_NETWORK_BACKUP_PATH = join(DATA_FOLDER, 'coordinator_backup.json')
export const DEFAULT_TOKENS_BACKUP_PATH = join(DATA_FOLDER, 'tokens_backup.nvm3')
export const DEFAULT_TOKENS_INFO_PATH = join(DATA_FOLDER, 'tokens_info.json')

if (!existsSync(DATA_FOLDER)){
mkdirSync(DATA_FOLDER)
Expand Down
Loading

0 comments on commit 9173579

Please sign in to comment.