zigpy / zigpy-znp

TI CC2531, CC13x2, CC26x2 radio support for Zigpy and ZHA
GNU General Public License v3.0
144 stars 40 forks source link

how to send association request with zigpy-znp #152

Closed jbgod closed 2 years ago

jbgod commented 2 years ago

i can't find the document abot zigpy-znp.i read the source code. i find there is not RSP about the c.MAC only REQ find. i also try the c.ZDO.JoinReq.Req and c.ZDO.NwkAddrReq.Req but it is no work. i want to use my cc2652 work as an endpoit to join my normal zigbee gateway.

puddly commented 2 years ago

Zigpy currently focuses on managing the device as a coordinator, not a router, so this isn't directly supported.

However, you can use the low-level operations in zigpy-znp to join an existing network:

import sys
import asyncio
import logging

import zigpy.types as t
import zigpy.exceptions
import zigpy.zdo.types as zdo_t
import zigpy_znp.commands as c
import zigpy_znp.types as znp_t

from zigpy_znp.tools.common import setup_parser
from zigpy_znp.zigbee.application import (
    ControllerApplication as ZNPControllerApplication,
)

LOGGER = logging.getLogger(__name__)

async def test_join_as_router(radio_path):
    app = ZNPControllerApplication({"device": {"path": radio_path}})
    await app.connect()

    try:
        # Always throws `NetworkNotFormed`, needs to be fixed for routers
        await app.load_network_info()
    except zigpy.exceptions.NetworkNotFormed:
        await app.form_network()

    if app.state.node_info.logical_type != zdo_t.LogicalType.Router:
        await app.write_network_info(
            node_info=app.state.node_info.replace(
                logical_type=zdo_t.LogicalType.Router, nwk=0x1234
            ),
            network_info=app.state.network_info,
        )

    joined_as_router = app._znp.wait_for_response(
        c.ZDO.StateChangeInd.Callback(State=znp_t.DeviceState.JoinedAsRouter)
    )

    # Run `zigpy_znp/tools/network_scan.py` to scan for networks
    await app._znp.request(
        c.ZDO.JoinReq.Req(
            LogicalChannel=25,
            PanId=0xABCD,
            ExtendedPanId=t.EUI64.convert("AA:BB:CC:DD:AA:BB:CC:DD"),
            ChosenParent=0x380A,
            Depth=1,
            StackProfile=2,
        ),
        callback=c.ZDO.JoinCnf.Callback(partial=True),
    )

    # TODO: how do you re-join a network? The network key is not stored in NVRAM...
    await joined_as_router
    await asyncio.sleep(60)
    await app.shutdown()

async def main(argv):
    parser = setup_parser("Test joining a network as a router")

    args = parser.parse_args(argv)

    await test_join_as_router(args.serial)

if __name__ == "__main__":
    asyncio.run(main(sys.argv[1:]))  # pragma: no cover

After the device joined my network, however, it did not respond to the coordinator's node descriptor request and I can't figure out how to re-join a network. Z-Stack's serial MT interface isn't documented very well so you will have to figure out how to get router mode properly working (if it's even possible using the coordinator firmware). If you do, please post an update!

jbgod commented 2 years ago

oh thank you!!! what is the capture struct for the transport key packet, i also want to capture the packet. i read many classes in the zdo,but i can't sure what is that

Zigpy currently focuses on managing the device as a coordinator, not a router, so this isn't directly supported.

However, you can use the low-level operations in zigpy-znp to join an existing network:

import sys
import asyncio
import logging

import zigpy.types as t
import zigpy.exceptions
import zigpy.zdo.types as zdo_t
import zigpy_znp.commands as c
import zigpy_znp.types as znp_t

from zigpy_znp.tools.common import setup_parser
from zigpy_znp.zigbee.application import (
    ControllerApplication as ZNPControllerApplication,
)

LOGGER = logging.getLogger(__name__)

async def test_join_as_router(radio_path):
    app = ZNPControllerApplication({"device": {"path": radio_path}})
    await app.connect()

    try:
        # Always throws `NetworkNotFormed`, needs to be fixed for routers
        await app.load_network_info()
    except zigpy.exceptions.NetworkNotFormed:
        await app.form_network()

    if app.state.node_info.logical_type != zdo_t.LogicalType.Router:
        await app.write_network_info(
            node_info=app.state.node_info.replace(
                logical_type=zdo_t.LogicalType.Router, nwk=0x1234
            ),
            network_info=app.state.network_info,
        )

    joined_as_router = app._znp.wait_for_response(
        c.ZDO.StateChangeInd.Callback(State=znp_t.DeviceState.JoinedAsRouter)
    )

    # Run `zigpy_znp/tools/network_scan.py` to scan for networks
    await app._znp.request(
        c.ZDO.JoinReq.Req(
            LogicalChannel=25,
            PanId=0xABCD,
            ExtendedPanId=t.EUI64.convert("AA:BB:CC:DD:AA:BB:CC:DD"),
            ChosenParent=0x380A,
            Depth=1,
            StackProfile=2,
        ),
        callback=c.ZDO.JoinCnf.Callback(partial=True),
    )

    # TODO: how do you re-join a network? The network key is not stored in NVRAM...
    await joined_as_router
    await asyncio.sleep(60)
    await app.shutdown()

async def main(argv):
    parser = setup_parser("Test joining a network as a router")

    args = parser.parse_args(argv)

    await test_join_as_router(args.serial)

if __name__ == "__main__":
    asyncio.run(main(sys.argv[1:]))  # pragma: no cover

After the device joined my network, however, it did not respond to the coordinator's node descriptor request and I can't figure out how to re-join a network. Z-Stack's serial MT interface isn't documented very well so you will have to figure out how to get router mode properly working (if it's even possible using the coordinator firmware). If you do, please post an update!

puddly commented 2 years ago

I didn't see one, nor could I find the key in NVRAM. I think there is something that must be done after the join request succeeds.

You should be able to sniff it being sent over-the-air though.

jbgod commented 2 years ago

i had try to use killerbee to forge an end device,which send and recv capture at sametime. but the association response is reply in 0.0004 second,but the chip deal with the capture too slow. i cant cap it. i use an cc2531 dongle on my windows capture the packet when i control a dongle communicate with a zigbee router tomorrow i will try this code when i back to my Lab. could u leave a email for me to communicate with u,i cost two week about this problem.

jbgod commented 2 years ago

there is something wrong with the code i should change my zigpy-znp version? image

puddly commented 2 years ago

My mistake. The above code depends on the new radio API: https://github.com/zigpy/zigpy-cli/pull/2

jbgod commented 2 years ago

` async def connect_network( path: str, beacon: t.structs.Beacon )-> None:

app = ZNPControllerApplication({"device": {"path": path}})
await app.connect()

try:
    # Always throws `NetworkNotFormed`, needs to be fixed for routers
    await app.load_network_info()
except zigpy.exceptions.NetworkNotFormed:
    await app.form_network()

if app.state.node_info.logical_type != zdo_t.LogicalType.Router:
    await app.write_network_info(
        node_info=app.state.node_info.replace(
            logical_type=zdo_t.LogicalType.Router, nwk=0x1234
        ),
        network_info=app.state.network_info,
    )

joined_as_router = app._znp.wait_for_response(
    c.ZDO.StateChangeInd.Callback(State=znp_t.DeviceState.JoinedAsRouter)
)

await app._znp.request(
    c.SYS.StackTune.Req(
        Operation = StackTuneOperation.SetRxOnWhenIdle,
        Value = 0x01,
    ),
    RspValue=0x01,
)    

# Run `zigpy_znp/tools/network_scan.py` to scan for networks
await app._znp.request(
    c.ZDO.JoinReq.Req(
        LogicalChannel = beacon.Channel,
        PanId = beacon.PanId,
        ExtendedPanId = beacon.ExtendedPanId,
        ChosenParent = beacon.Src,
        Depth = beacon.Depth,
        StackProfile = beacon.StackProfile
    ),
    RspStatus=t.Status.SUCCESS,
)

# TODO: how do you re-join a network? The network key is not stored in NVRAM...
await joined_as_router
await asyncio.sleep(60)
await app.shutdown()

` i had install your new branch,and i add this function to the zigpy_znp/tools/network_scan.py i can't change the 'Rx on when Idle' there is something wrong when i try to change the 'Rx on when Idle',it is alway be 0x00 image

jbgod commented 2 years ago

the error show i should set the callback to status,so i change the callback of c.ZDO.JoinReq.Req image

puddly commented 2 years ago

Z-Stack has two types of requests:

  1. Request/response: you can use rsp = await znp.request(Some.Req(), OptionalRspValidator=123): https://github.com/zigpy/zigpy-znp/blob/19df5c01e9f4675ad1e206446575c4a02e28c073/zigpy_znp/zigbee/application.py#L229
  2. Request/acknowledgement/callback: you can use callback = await znp.request_callback_rsp(Some.Req(), RspOptionalValidator=123, callback=...): https://github.com/zigpy/zigpy-znp/blob/19df5c01e9f4675ad1e206446575c4a02e28c073/zigpy_znp/zigbee/application.py#L517

If your command returns a response and then a callback, you would use request_callback_rsp. Otherwise, use request.

jbgod commented 2 years ago

how can i define the "Rx on When Idle" in the packet which i send to the coordinator the normal zigbee device send the packet with Rx on When Idle = 0x01 image i use c.ZDO.JoinReq.Req send the association request with this on,and without the transport key packet back the transport key packet contain the network key of coordinator image

puddly commented 2 years ago

@jbgod how did you resolve this?

jbgod commented 2 years ago

i can't solve this by zigpy. i back to try solve this by killerbee https://github.com/riverloopsec/killerbee/issues/256