Skip to content

Commit

Permalink
Add specific logic for Sonoff ZBDongle-E Router.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec committed Oct 29, 2024
1 parent b5d2052 commit e3604af
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 44 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ $ npm install -g ember-zli
$ ember-zli COMMAND
running command...
$ ember-zli (--version)
ember-zli/2.6.2 win32-x64 node-v20.15.0
ember-zli/2.6.3 win32-x64 node-v20.15.0
$ ember-zli --help [COMMAND]
USAGE
$ ember-zli COMMAND
Expand Down Expand Up @@ -108,7 +108,7 @@ EXAMPLES
$ ember-zli bootloader
```

_See code: [src/commands/bootloader/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.2/src/commands/bootloader/index.ts)_
_See code: [src/commands/bootloader/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.3/src/commands/bootloader/index.ts)_

## `ember-zli help [COMMAND]`

Expand Down Expand Up @@ -145,7 +145,7 @@ EXAMPLES
$ ember-zli router
```

_See code: [src/commands/router/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.2/src/commands/router/index.ts)_
_See code: [src/commands/router/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.3/src/commands/router/index.ts)_

## `ember-zli sniff`

Expand All @@ -162,7 +162,7 @@ EXAMPLES
$ ember-zli sniff
```

_See code: [src/commands/sniff/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.2/src/commands/sniff/index.ts)_
_See code: [src/commands/sniff/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.3/src/commands/sniff/index.ts)_

## `ember-zli stack`

Expand All @@ -179,7 +179,7 @@ EXAMPLES
$ ember-zli stack
```

_See code: [src/commands/stack/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.2/src/commands/stack/index.ts)_
_See code: [src/commands/stack/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.3/src/commands/stack/index.ts)_

## `ember-zli utils`

Expand All @@ -196,7 +196,7 @@ EXAMPLES
$ ember-zli utils
```

_See code: [src/commands/utils/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.2/src/commands/utils/index.ts)_
_See code: [src/commands/utils/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.6.3/src/commands/utils/index.ts)_

## `ember-zli version`

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ember-zli",
"description": "Interact with EmberZNet-based adapters using zigbee-herdsman 'ember' driver",
"version": "2.6.2",
"version": "2.6.3",
"author": "Nerivec",
"bin": {
"ember-zli": "bin/run.js"
Expand Down
44 changes: 43 additions & 1 deletion src/commands/bootloader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ import { AdapterModel, FirmwareVariant } from '../../utils/types.js'
const SUPPORTED_VERSIONS_REGEX = /(7\.4\.\d\.\d)|(8\.0\.\d\.\d)/
const FIRMWARE_EXT = '.gbl'

const clearNVM3SonoffZBDongleE: () => Buffer = () => {
const start = 'eb17a603080000000000000300000000f40a0af41c00000000000000000000000000000000000000000000000000000000000000fd0303fd0480000000600b00'
const blankChunkStart = '01009ab2010000d0feffff0fffffffff0098'
const blankChunkLength = 8174
const end = 'fc0404fc040000004b83c4aa'

return Buffer.concat([
Buffer.from(start, 'hex'),
Buffer.from(blankChunkStart, 'hex'),
Buffer.alloc(blankChunkLength, 0xff),
Buffer.from(blankChunkStart, 'hex'),
Buffer.alloc(blankChunkLength, 0xff),
Buffer.from(blankChunkStart, 'hex'),
Buffer.alloc(blankChunkLength, 0xff),
Buffer.from(blankChunkStart, 'hex'),
Buffer.alloc(blankChunkLength, 0xff),
Buffer.from(end, 'hex'),
])
}

const CLEAR_NVM3_BUFFERS: Partial<Record<AdapterModel, () => Buffer>> = {
'Sonoff ZBDongle-E': clearNVM3SonoffZBDongleE,
'Sonoff ZBDongle-E - ROUTER': clearNVM3SonoffZBDongleE,
}

export default class Bootloader extends Command {
static override args = {}
static override description = 'Interact with the Gecko bootloader in the adapter.'
Expand Down Expand Up @@ -96,7 +121,7 @@ export default class Bootloader extends Command {
choices: [
{ name: 'Get info', value: BootloaderMenu.INFO },
{ name: 'Update firmware', value: BootloaderMenu.UPLOAD_GBL },
{ name: 'Clear NVM3', value: BootloaderMenu.CLEAR_NVM3, disabled: gecko.adapterModel !== 'Sonoff ZBDongle-E' },
{ name: 'Clear NVM3', value: BootloaderMenu.CLEAR_NVM3, disabled: !this.supportsClearNVM3(gecko.adapterModel) },
{ name: 'Exit bootloader', value: BootloaderMenu.RUN },
{ name: 'Force close', value: -1 },
],
Expand All @@ -122,6 +147,9 @@ export default class Bootloader extends Command {
return false
}
}
} else if (answer === BootloaderMenu.CLEAR_NVM3) {
// adapterModel is defined here since menu is disabled if not supported, same for the value in the object
firmware = CLEAR_NVM3_BUFFERS[gecko.adapterModel!]!()
}

return gecko.navigate(answer, firmware)
Expand Down Expand Up @@ -235,4 +263,18 @@ export default class Bootloader extends Command {
}
}
}

private supportsClearNVM3(adapterModel?: AdapterModel): boolean {
if (!adapterModel) {
return false
}

for (const key in CLEAR_NVM3_BUFFERS) {
if (key === adapterModel) {
return true
}
}

return false
}
}
53 changes: 25 additions & 28 deletions src/utils/bootloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,8 @@ const GBL_END_TAG = Buffer.from([0xfc, 0x04, 0x04, 0xfc])
const GBL_METADATA_TAG = Buffer.from([0xf6, 0x08, 0x08, 0xf6])
const VALID_FIRMWARE_CRC32 = 558161692

const NVM3_INIT_START =
'eb17a603080000000000000300000000f40a0af41c00000000000000000000000000000000000000000000000000000000000000fd0303fd0480000000600b00'
const NVM3_INIT_BLANK_CHUNK_START = '01009ab2010000d0feffff0fffffffff0098'
const NVM3_INIT_BLANK_CHUNK_LENGTH = 8174
const NVM3_INIT_END = 'fc0404fc040000004b83c4aa'
const FORCE_RESET_SUPPORT_ADAPTERS: ReadonlyArray<AdapterModel> = ['Sonoff ZBDongle-E', 'Sonoff ZBDongle-E - ROUTER']
const ALWAYS_FORCE_RESET_ADAPTERS: ReadonlyArray<(typeof FORCE_RESET_SUPPORT_ADAPTERS)[number]> = ['Sonoff ZBDongle-E - ROUTER']

export enum BootloaderEvent {
FAILED = 'failed',
Expand Down Expand Up @@ -109,13 +106,13 @@ export class GeckoBootloader extends EventEmitter<GeckoBootloaderEventMap> {
}
| undefined

constructor(portConf: PortConf, adapter?: AdapterModel) {
constructor(portConf: PortConf, adapterModel?: AdapterModel) {
super()

this.state = BootloaderState.NOT_CONNECTED
this.waiter = undefined
this.portConf = portConf
this.adapterModel = adapter
this.adapterModel = adapterModel
// override config to default for serial gecko bootloader
this.transport = new Transport({
...this.portConf,
Expand Down Expand Up @@ -198,6 +195,13 @@ export class GeckoBootloader extends EventEmitter<GeckoBootloaderEventMap> {
}

case BootloaderMenu.CLEAR_NVM3: {
if (firmware === undefined) {
logger.error(`Navigating to clear NVM3 requires a valid firmware.`, NS)
await this.transport.close(false) // don't emit closed since we're returning true which will close anyway

return true
}

const confirmed = await confirm({
default: false,
message: 'Confirm NVM3 clearing? (Cannot be undone.)',
Expand All @@ -208,28 +212,16 @@ export class GeckoBootloader extends EventEmitter<GeckoBootloaderEventMap> {
return false
}

return this.menuUploadGBL(
Buffer.concat([
Buffer.from(NVM3_INIT_START, 'hex'),
Buffer.from(NVM3_INIT_BLANK_CHUNK_START, 'hex'),
Buffer.alloc(NVM3_INIT_BLANK_CHUNK_LENGTH, 0xff),
Buffer.from(NVM3_INIT_BLANK_CHUNK_START, 'hex'),
Buffer.alloc(NVM3_INIT_BLANK_CHUNK_LENGTH, 0xff),
Buffer.from(NVM3_INIT_BLANK_CHUNK_START, 'hex'),
Buffer.alloc(NVM3_INIT_BLANK_CHUNK_LENGTH, 0xff),
Buffer.from(NVM3_INIT_BLANK_CHUNK_START, 'hex'),
Buffer.alloc(NVM3_INIT_BLANK_CHUNK_LENGTH, 0xff),
Buffer.from(NVM3_INIT_END, 'hex'),
]),
)
return this.menuUploadGBL(firmware)
}
}
}

public async resetByPattern(exit: boolean): Promise<void> {
public async forceReset(exit: boolean): Promise<void> {
switch (this.adapterModel) {
// TODO: support per adapter
case 'Sonoff ZBDongle-E': {
case 'Sonoff ZBDongle-E':
case 'Sonoff ZBDongle-E - ROUTER': {
await this.transport.serialSet({ dtr: false, rts: true })
await this.transport.serialSet({ dtr: true, rts: false }, 100)

Expand Down Expand Up @@ -381,14 +373,19 @@ export class GeckoBootloader extends EventEmitter<GeckoBootloaderEventMap> {
await this.transport.initPort()

// on first knock only, try pattern reset if supported
if (!fail && this.adapterModel === 'Sonoff ZBDongle-E') {
const forceReset = await confirm({ message: 'Force reset into bootloader?', default: true })
if (!fail && this.adapterModel && FORCE_RESET_SUPPORT_ADAPTERS.includes(this.adapterModel)) {
// XXX: always force reset Sonoff ZBDongle-E Router to prevent issues with EZSP 6.10.3 (can be removed once versions updated and no longer used)
const forceReset =
ALWAYS_FORCE_RESET_ADAPTERS.includes(this.adapterModel) ||
(await confirm({ message: 'Force reset into bootloader?', default: true }))

if (forceReset) {
await this.resetByPattern(false)
logger.debug(`Entering bootloader via force reset.`, NS)

await this.forceReset(false)

if (this.state === BootloaderState.IDLE) {
logger.debug(`Entered bootloader via pattern reset.`, NS)
// nothing else to do if already got the bl prompt
return
}
}
Expand Down Expand Up @@ -441,7 +438,7 @@ export class GeckoBootloader extends EventEmitter<GeckoBootloaderEventMap> {
// got menu back, failed to run
logger.warning(`Failed to exit bootloader and run firmware. Trying pattern reset...`, NS)

await this.resetByPattern(true)
await this.forceReset(true)
}

return true
Expand Down
8 changes: 4 additions & 4 deletions src/utils/cpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
CPC_SYSTEM_REBOOT_MODE_BOOTLOADER,
} from './consts.js'
import { Transport, TransportEvent } from './transport.js'
import { CpcSystemCommand, CpcSystemCommandId, CpcSystemStatus, Digit, FirmwareVersionShort, PortConf } from './types.js'
import { CpcSystemCommand, CpcSystemCommandId, CpcSystemStatus, FirmwareVersionShort, PortConf } from './types.js'
import { computeCRC16 } from './utils.js'

const NS = { namespace: 'cpc' }
Expand Down Expand Up @@ -77,9 +77,9 @@ export class Cpc extends EventEmitter<CpcEventMap> {
}

// const propertyId = result.payload.readUInt32LE(0)
const major = result.payload.readUInt32LE(4) as Digit
const minor = result.payload.readUInt32LE(8) as Digit
const patch = result.payload.readUInt32LE(12) as Digit
const major = result.payload.readUInt32LE(4)
const minor = result.payload.readUInt32LE(8)
const patch = result.payload.readUInt32LE(12)

return `${major}.${minor}.${patch}`
}
Expand Down
7 changes: 3 additions & 4 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { EUI64 } from 'zigbee-herdsman/dist/zspec/tstypes.js'

import { BAUDRATES } from './consts.js'

export type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

export type AdapterModel =
| 'Aeotec Zi-Stick (ZGA008)'
| 'EasyIOT ZB-GW04 v1.1'
Expand All @@ -15,6 +13,7 @@ export type AdapterModel =
| 'SMLight SLZB07'
| 'SMLight SLZB07mg24'
| 'Sonoff ZBDongle-E'
| 'Sonoff ZBDongle-E - ROUTER'
| 'SparkFun MGM240p'
| 'TubeZB MGM24'
| 'TubeZB MGM24PB'
Expand All @@ -34,8 +33,8 @@ export type EmberFullVersion = { ezsp: number; revision: string } & EmberVersion
export type ConfigValue = { [key: string]: string }

export type FirmwareVariant = 'latest' | 'official' | 'recommended' | 'experimental'
export type FirmwareVersion = `${Digit}.${Digit}.${Digit}.${Digit}`
export type FirmwareVersionShort = `${Digit}.${Digit}.${Digit}`
export type FirmwareVersion = `${number}.${number}.${number}.${number}`
export type FirmwareVersionShort = `${number}.${number}.${number}`
export type FirmwareFilename = `${string}.gbl`
export type FirmwareURL = `https://${string}/${FirmwareFilename}`

Expand Down

0 comments on commit e3604af

Please sign in to comment.