Skip to content

Python recipes

Needs pyserial. Conventions: DST_LEFT = 0x50, DST_RIGHT = 0x51; transact(port, dst, type_, c0, c1, params, timeout) (dst is positional); Session(port, dst, timeout) performs the 30/1001 handshake — 30/10xx needs it on the same open handle.

Minimal frame (illustrative sketch)

def build_frame(dst, type_, c0, c1, params=b""):
    body = bytes([type_, len(params) + 2, c0, c1]) + bytes(params)
    crc = 0
    for b in body[1:]:      # XOR from C0 through end of payload
        crc ^= b
    return b"\xaa\x00" + bytes([dst, 0]) + body + bytes([crc, 0x04])

def parse_frame(f):
    assert f[0] == 0xAA and f[-1] == 0x04
    crc = 0
    for b in f[6:-2]:
        crc ^= b
    assert crc == f[-2], "CRC mismatch"
    return {"src": f[1], "type": f[4], "c0": f[6], "c1": f[7],
            "payload": f[8:-2]}

Session + reads

from cdc_client import Session, DST_LEFT  # names illustrative

ses = Session("/dev/cu.usbmodem1101", DST_LEFT)  # handshake inside
info = ses.cmd(0xFA, 0x10, 0x01, b"")            # fa/1001 device info
fw   = ses.cmd(0xFE, 0x10, 0x02, b"")            # fe/1002 → 00 00 03 29 00 38 = 0.3.41.0
layer0 = ses.read_layer(0)                        # 30/1003 multipart, both parts

Writes (same-length only!)

# key write, layer-aware ACK (accept 00 00 OR 00 <layer>)
ack = ses.write_key(record_bytes, layer=1)
assert len(ack) == 2 and ack[0] == 0x00 and ack[1] in (0x00, layer)

# LED single-entry write: [00, layer] + [KK, H_lo, H_hi, S]
ses.cmd(0x30, 0x10, 0x0E, bytes([0x00, layer, kk, h & 0xFF, h >> 8, s]))

# ED: ALWAYS [target, value] (0xFF = all). 1-byte sends write ZEROS to NVS.
ses.cmd(0xED, 0x10, 0x13, bytes([0xFF, 100]))  # 1013 ceiling = 100

Snapshot & restore (CLI)

python3 toolkit/naya-backup.py                      # → research/dumps/*.json
python3 toolkit/naya-restore.py --snap snap.json    # dry run
python3 toolkit/naya-restore.py --snap snap.json --apply   # writes
python3 toolkit/naya-undark.py --apply              # LED revival (left)
python3 toolkit/naya-maxbrt.py --level 100 --apply  # 1013 ceiling

Factory format (destructive — read the risks first)

# left port, dst 0x50: type 0x30 c0 0x10 c1 0xCA params [01]
resp = transact(port, 0x50, 0x30, 0x10, 0xCA, b"\x01")
# expect status 00; afterwards 30/1001 + 30/1003 answer error 16 00 (empty store)

See factory reset and the recovery cookbook.