Skip to content

[REQUEST] ZGP (Zigbee Green Power) with zigpy-znp and Texas Instruments Z-Stack 3 #47

Closed as not planned

Description

@Hedda

@puddly Requesting support for ZGP (Zigbee Green Power) using zigpy-znp with Texas Instruments Z-Stack 3 Zigbee coordinator.

FYI, @zoic21 is trying to implement ZGP (Zigbee Green Power) device support into zigpy, however wrote that is only developing it using bellows and zigpy-deconz as he currently has no coordinator for zigpy-znp. See -> zigpy/zigpy#519 and zigpy/zigpy#656

PS: zoic21 is currently using EnOcean PTM 215ZE (PTM215ZE) based ZGP switch as reference but it should maybe be noted that it looks like the newer EnOcean PTM 216Z (PTM216Z) might be more common use, please see -> zigpy/zha-device-handlers#478

PPS: There is more info about ZGP (Zigbee Green Power) and devices supporting it in the zigpy request -> zigpy/zigpy#341

Activity

  1. puddly commented on Nov 12, 2020

    @puddly
    Member

    I can add the required endpoint but I have no way to actually test this out. Do you know of any cheaper ZGP devices? $30 for just a module is a little steep.

  2. Hedda commented on Nov 18, 2020

    @Hedda
    ContributorAuthor

    There are links to a number of ZGP devices in zigpy/zigpy#341 but unfortunately there looks to be no 'inexpensive' ones as of yet.

    Maybe find a used one or might be possible to one on sale during next weeks Black Week and Black Friday or Cyber Monday?

    Regardless it would probably be most useful to get a remote control model or a wireless switch model instead of only a module.

    Most common examples of Green Powered devices for home automation are rocker wall switches:

  3. Ratief commented on Dec 8, 2020

    @Ratief

    I would be willing to buy you a ZGP device if it would help get this working. Personally I love the Hue Taps and they are the last device that I need to migrate away from my bridge.

  4. Kazymire commented on Feb 15, 2021

    @Kazymire

    yes i am very interesting in zgpd too, now i using cc usb dongle with z2m, but i see that with conbbe II, we can see gpd trame. but not with cc dongle.
    As Ratief, it's the last fonction i need, to migrate on zigpy
    Best regard

  5. Hedda commented on Feb 15, 2021

    @Hedda
    ContributorAuthor

    FYI, @zoic21 have made progress with ZGP for zigpy and is requesting regression testing of his new pull request in zigpy/zigpy#656

    Can someone can test with this code if all it's ok ? No side effect. If it I think we can merge this first step (after I fix coveralls).

    That is, he is asking people do functional and non-functional tests to ensure that his patch does not break any existing functions. If it does not break anything obvious in zigpy then it could possible be merged 'as-is' even if ZGP might be be fully production ready.

  6. sepahewe commented on Mar 4, 2021

    @sepahewe

    @puddly Would you still be helped by a device? If so, PM me on twitter (see my profile) and I'll send you one.

  7. omerk commented on Mar 10, 2021

    @omerk

    @puddly Happy to contribute to the R&D budget for this exploration, can also send a few of the CC2652R/P sticks if you need spares/test devices?

  8. Hedda commented on Mar 24, 2021

    @Hedda
    ContributorAuthor

    Happy to contribute to the R&D budget for this exploration, can also send a few of the CC2652R/P sticks if you need spares/test devices?

    @zoic21 @sarakha63 Do you have Texas Instruments Z-Stack 3 ZNP coordinators to test with your zigpy/zigpy#656 PR for zigpy?

  9. zoic21 commented on Mar 24, 2021

    @zoic21

    No we only focus for now on deconz and EZSP but after integration on this type of key we can manage integreation on ZNP.

  10. Hedda commented on Oct 31, 2026

    @Hedda
    ContributorAuthor

    Bump this request now ZGP (Zigbee Green Power) frame RX and packet forwarding has been added to bellows, zigpy-deconz, and ziggurat:

    Texas Instruments CC2652P is known among the most commonly used Zigbee Coordinaor adapter, (second only to EFR32MG21/EFR32MG24).

  11. puddly commented on Oct 31, 2026

    @puddly
    Member

    I tested this a few weeks ago and I found no way to enable GP reception in Z-Stack. It just isn't compiled in. If you want Green Power support, use either https://github.com/zigpy/bellows or https://github.com/zigpy/zigpy-ziggurat.

  12. Hedda commented on Oct 31, 2026

    @Hedda
    ContributorAuthor

    I tested this a few weeks ago and I found no way to enable GP reception in Z-Stack

    Hmm, I am pretty sure I read that Zigbee2MQTT does support Zigbee Green Power devices via the z-stack adapter in zigbee-herdsman?

    Their documentation does at least not specifcially mention that zStack based (Texas Instruments) adapters have any such limitations?

    You can also find Koenkk's original implementation announcement here from 2020 (back when Z2M more or less only supported Z-Stack):

    From the discussion here it at least sounds as if they got Zigbee Green Power support working with the zstack adapter in zigbee-herdsman.

  13. puddly commented on Oct 31, 2026

    @puddly
    Member

    You can use ZGP even if the coordinator itself doesn't support receiving GPDFs directly: you just need to have a Zigbee 3.0 router on the network, which will tunnel the frames encapsulated in a ZCL cluster command. This issue is tracking GP reception support for TI radios and as far as I can tell, it just isn't possible to receive GP frames with a TI coordinator using the Z2M firmware builds, you need a router to proxy it.

    The only two radios that can properly handle Green Power are SiLabs (via bellows) and Ziggurat. deCONZ has partial support but has debouncing issues with unencrypted frames.

  14. Hedda commented on Nov 1, 2026

    @Hedda
    ContributorAuthor

    The only two radios that can properly handle Green Power are SiLabs (via bellows) and Ziggurat. deCONZ has partial support but has debouncing issues with unencrypted frames.

    Should that then maybe be mentioned under limitations in ZHA docs?

    Noticed that next release will removed ZGP section under limitation:

    Perhaps add something new under ZHA limitations like example this:

    ZGP (Zigbee Green Power) limitation on some radios

    The only two types of radio adapters that can handle ZGP (Zigbee Green Power) direct reception today in ZHA are Silicon Labs (via bellows) and Ziggurat. The deCONZ based adapters do also have partial support for ZGP but have issues with unencrypted frames. You can still use ZGP with other radios if you have a Zigbee 3.0 router on your network which will proxy the ZGP packages._

    Probably a good idea to be clear about that, especially if not planned.

    PS: Another related limitation is can not use ZGP (Zigbee Green Power) devices then if happen to use a TI Z-Stack 1.2 coordinator adapter:

  15. Hedda commented on Nov 1, 2026

    @Hedda
    ContributorAuthor

    @puddly I have now promted AI (Claude Opus 5.5 Max) to dig into this request for gaps in zigpy-znp ZGP support and analyze zstack adapter in their zigbee-herdsman + research Zigbee2MQTT's previous discussions, while comparing it to your implementations in bellows, zigpy-deconz and zigpy-ziggurat. Got this result back copied below with suggestions on what is possibly missing in zigpy-znp as well as a patch:

    TL;DR

    • Z-Stack ZNP firmware can't pass the GPDFs it hears to the host. TI said so when Koenkk asked, and nothing has changed in the current SDK or in Koenkk's patches.
    • zigbee-herdsman gets Green Power on Z-Stack only from GP Notification and GP Commissioning Notification commands tunneled by GP proxies. It never uses the MT_GP subsystem.
    • zigpy-znp needs to join the GP group, tag GP-cluster frames with the GP profile, and refuse aliased sends. Patch and description below.
    • Direct reception at the znp coordinator, as bellows, zigpy-deconz and ziggurat now do it, would need a new ZNP firmware first.

    Koenkk never got Z-Stack to receive Green Power (GP) frames either. zigbee-herdsman’s Z-Stack adapter only sees GP frames that a GP-capable router (a “proxy”) has already received and repackaged as ordinary Zigbee messages. The ZNP firmware never hands the host a GP frame the coordinator heard itself. zigpy can now take that same router path, but zigpy-znp still needs three small fixes for the router path to work.

    Download patch for zigpy-znp to fix router path:

    zigpy-znp-green-power-proxy-path.patch

    What zigbee-herdsman really does on Z-Stack

    • Two setup steps, nothing else firmware-specific. It registers endpoint 242 with the GP profile 0xA1E0 (endpoints.ts). It also adds endpoint 242 to the GP group 0x0B84 with extFindGroup/extAddGroup (manager.ts:101). Both came in Koenkk’s original “Green Power support” commit, April 2020.
    • No use of TI’s GP host interface. It never uses the MT_GP subsystem; there’s one secReq definition that nothing calls. Commissioning mode, notification parsing and pairing all live in the adapter-agnostic greenPower.ts, which works on messages forwarded by routers.
    • TI told Koenkk the ZNP can’t forward GP frames. In 2022 he asked TI how to get Hue Tap commissioning frames out of a CC26x2 ZNP. The ZNP project defines DISABLE_GREENPOWER_BASIC_PROXY, which removes the GP task (gp_event_loop) from the firmware. Without that task, the GP receive layer never reaches MT_GPDataInd, and removing the flag breaks the build. TI’s conclusion was that the SDK 6.30 ZNP will not forward GP messages to any host.
    • Nothing has changed since.

    So puddly’s conclusion matches TI’s own answer. The ZHA docs for 2026.11 say the same thing: direct GP for ziggurat, ezsp (bellows) and deconz, while znp only works through routers.

    What zigpy-znp needs for the router path

    zigpy 2.4 already registers endpoint 242 through register_endpoints(), and permit() already tells routers to start GP commissioning. The gaps are in zigpy-znp:

    1. Join the GP group.
      • After commissioning, zigpy calls subscribe_to_multicast_group(0x0B84, endpoint_id=242), but zigpy-znp doesn’t implement it, so nothing happens.
      • Routers send button presses to group 0x0B84, and stock Z-Stack drops group messages when no endpoint is a member.
      • The fix is ZDO.ExtAddGroup(242, 0x0B84), accepting “already exists” as success, plus ExtRemoveGroup for unsubscribing. This is the same thing herdsman does.
    2. Infer the GP profile.
      • Z-Stack’s incoming-message callback (AF.IncomingMsg) has no profile field, so zigpy-znp takes the profile from the destination endpoint.
      • Koenkk’s firmware delivers group messages for groups nobody joined to endpoint 1, so a GP notification arrives labelled with the HA profile.
      • zigpy only recognises forwarded GP frames when the profile is 0xA1E0, so it misses them and logs “Unknown device”.
      • The fix: anything from endpoint 242 on cluster 0x0021 gets profile 0xA1E0, whatever the destination endpoint.
    3. Refuse alias sends.
      • zigpy announces each GP device’s short address by sending as that address (an “alias”), and falls back if the radio raises NotImplementedError.
      • Z-Stack’s send commands have no alias parameter, and zigpy-znp silently ignores the alias instead of raising.
      • The fix is to raise NotImplementedError, as zigpy-deconz does.
    4. Housekeeping.
      • Require zigpy>=2.4.0.
      • The current test suite collapses on zigpy 2.4.1: 137 failed, against 167 passing on 2.3.0. Startup now sends a GP commissioning broadcast that the fake ZNP server doesn’t answer, and later message sequence numbers shift.

    zigpy-znp-green-power-proxy-path.patch implements all four, against zigpy-znp dev (926626c):

    • The full suite passes on zigpy 2.4.1 (412 tests).
    • 12 of the 16 new GP tests fail on current dev, so they catch the bugs above.
    • One new test runs the whole router path: commissioning notification in, device created, group joined, pairing sent, then a forwarded Toggle press delivered.
    • It’s only been tested against the fake server, not real hardware.

    What direct reception at znp coordinator would need

    Direct reception at the znp coordinator, which bellows, zigpy-deconz and ziggurat now have, needs new firmware.

    So the Z-Stack 3.x.0 firmware patch would have to change first: a ZNP build without DISABLE_GREENPOWER_BASIC_PROXY, with the build errors TI mentioned resolved. TI’s MT_GP host interface (MT_GP_CB_FUNC) is already compiled into the stock ZNP build. With such firmware, zigpy-znp would also need to:

    • Fix the ZGP.DataInd definition.
      • zigpy-znp currently describes it as raw radio-frame fields from the old MT API document.
      • SimpleLink’s MT_GPDataInd actually sends a decoded GP frame, ending with the payload and the MIC (the frame’s authentication code).
      • The SecReq, SecRsp, DataReq and DataCnf definitions already match.
    • Handle security requests. Answer each ZGP.SecReq with a ZGP.SecRsp. Either pass the frame through unprocessed so zigpy checks the MIC itself, as bellows does, or return the key zigpy holds.
    • Hand frames to zigpy. Map the firmware’s status codes to zigpy’s security status and call gp_packet_received(), mirroring bellows’ _handle_gp_frame.
    • Optionally, transmit to GP devices. ZGP.DataReq could let the coordinator itself send a commissioning reply to a GP device. zigpy has no radio hook for that yet; its GP code carries a TODO for every radio.

    Until such Z-Stack 3.x.0 firmware exists, ZNP users need a GP-capable router, such as Philips Hue lights, in range of each GP switch.

    Full patch

    The full patch also adds ExtFindGroup/ExtAddGroup/ExtRemoveGroup and GP broadcast handlers to the fake server in tests/conftest.py, updates test_endpoints and test_permit_join, and adds tests/application/test_green_power.py. On dev (926626c) with zigpy 2.4.1:

    diff --git a/pyproject.toml b/pyproject.toml
    index d3153d2..3e4067d 100644
    --- a/pyproject.toml
    +++ b/pyproject.toml
    @@ -14,7 +14,7 @@ readme = "README.md"
     license = {text = "GPL-3.0"}
     requires-python = ">=3.11"
     dependencies = [
    -    "zigpy>=1.5.0",
    +    "zigpy>=2.4.0",
         "voluptuous",
         "coloredlogs",
         "jsonschema",
    diff --git a/zigpy_znp/zigbee/application.py b/zigpy_znp/zigbee/application.py
    index 30f33e4..ee8affc 100644
    --- a/zigpy_znp/zigbee/application.py
    +++ b/zigpy_znp/zigbee/application.py
    @@ -14,8 +14,10 @@ import zigpy.state
     import zigpy.types
     import zigpy.util
     import zigpy.zcl
    +from zigpy.zcl.clusters.greenpower import GreenPowerProxy
     import zigpy.zdo
     import zigpy.zdo.types as zdo_t
    +import zigpy.zgp
     
     from zigpy_znp.api import ZNP
     import zigpy_znp.commands as c
    @@ -101,6 +103,45 @@ class ControllerApplication(zigpy.application.ControllerApplication):
                 RspStatus=t.Status.SUCCESS,
             )
     
    +    async def _subscribe_to_multicast_group(
    +        self, group_id: zigpy.types.Group, endpoint_id: int
    +    ) -> None:
    +        """
    +        Adds an endpoint to a group in the Z-Stack APS group table. Without this,
    +        Z-Stack drops groupcasts (such as GP notifications to the GP group) unless the
    +        firmware is patched to forward groups that no endpoint is a member of.
    +        """
    +
    +        # The group table is stored in NVRAM, so the endpoint may already be a member.
    +        # The first byte of the group information is the status.
    +        rsp = await self._znp.request(
    +            c.ZDO.ExtFindGroup.Req(Endpoint=endpoint_id, GroupId=group_id)
    +        )
    +
    +        if rsp.Group[:1] == bytes([t.Status.SUCCESS]):
    +            return
    +
    +        await self._znp.request(
    +            c.ZDO.ExtAddGroup.Req(
    +                Endpoint=endpoint_id,
    +                GroupId=group_id,
    +                GroupName=t.CharacterString(""),
    +            ),
    +            RspStatus=t.Status.SUCCESS,
    +        )
    +
    +    async def _unsubscribe_from_multicast_group(
    +        self, group_id: zigpy.types.Group, endpoint_id: int
    +    ) -> None:
    +        """
    +        Removes an endpoint from a group in the Z-Stack APS group table.
    +        """
    +
    +        # A missing group is reported as a generic failure and can be ignored
    +        await self._znp.request(
    +            c.ZDO.ExtRemoveGroup.Req(Endpoint=endpoint_id, GroupId=group_id)
    +        )
    +
         async def load_network_info(self, *, load_devices=False) -> None:
             """
             Loads network information from NVRAM.
    @@ -519,8 +560,17 @@ class ControllerApplication(zigpy.application.ControllerApplication):
             Handler for all non-ZDO messages.
             """
     
    +        # `AF.IncomingMsg` has no profile ID so it has to be inferred. GP proxies send
    +        # tunneled GPDFs from the GP endpoint on the GP cluster, but firmware that
    +        # forwards groupcasts for groups no endpoint is a member of (Koenkk's builds)
    +        # delivers them to endpoint 1, whose profile would hide them from zigpy.
    +        if (
    +            msg.SrcEndpoint == zigpy.zgp.GP_ENDPOINT
    +            and msg.ClusterId == GreenPowerProxy.cluster_id
    +        ):
    +            profile = zigpy.profiles.zgp.PROFILE_ID
             # XXX: Is it possible to receive messages on non-assigned endpoints?
    -        if msg.DstEndpoint != 0 and msg.DstEndpoint in self._device.endpoints:
    +        elif msg.DstEndpoint != 0 and msg.DstEndpoint in self._device.endpoints:
                 profile = self._device.endpoints[msg.DstEndpoint].profile_id
             else:
                 LOGGER.warning("Received a message on an unregistered endpoint: %s", msg)
    @@ -875,6 +925,10 @@ class ControllerApplication(zigpy.application.ControllerApplication):
     
             LOGGER.debug("Sending packet %r", packet)
     
    +        if packet.alias_src is not None:
    +            # Z-Stack's AF data requests have no alias parameters
    +            raise NotImplementedError("Z-Stack cannot send packets with an alias")
    +
             options = c.af.TransmitOptions.SUPPRESS_ROUTE_DISC_NETWORK
     
             if zigpy.types.TransmitOptions.ACK in packet.tx_options:
    • The full suite passes (412 tests), and 12 of the 16 new tests fail without the fix.
    • One test runs the whole proxy path: Commissioning Notification → device created → GP group joined → Device_annce and GP Pairing broadcast → forwarded Toggle delivered to the device.
    • The tests run against all three fake devices (CC26X2R1, Z-Stack 3 CC2531, Z-Stack 1.2 CC2531). Nothing has been tested on hardware with a real proxy yet.

    zigpy 2.4 already registers endpoint 242 in register_endpoints() and opens GP commissioning from permit(). The gaps are in zigpy-znp:

    1. Join the GP group. zigpy calls subscribe_to_multicast_group(0x0B84, endpoint_id=242) at startup when GP devices exist and after each commissioning (manager.py), and unsubscribes after the last GPD is removed. zigpy-znp doesn't implement _subscribe_to_multicast_group(), so nothing happens. zigpy pairs GPDs with groupcast forwarding to 0x0B84, and stock Z-Stack drops groupcasts for groups that no endpoint is a member of. Fix: ZDO.ExtFindGroup, then ZDO.ExtAddGroup(242, 0x0B84) like herdsman, since the group table is in NV; and ZDO.ExtRemoveGroup to unsubscribe.
    2. Tag GP-cluster frames with the GP profile. AF.IncomingMsg has no profile ID, so on_af_message() takes it from the destination endpoint. Koenkk's firmware delivers groupcasts for groups with no member endpoint to endpoint 1, whatever their profile (af.c hunk). So today a GP Notification arrives tagged with the HA profile, is_gp_tunnel_packet() skips it, and zigpy logs "Unknown device" for the GPD's alias. With fix 1 these frames reach endpoint 242, but anything from endpoint 242 on cluster 0x0021 should get profile 0xA1E0 wherever it lands.
    3. Refuse aliased sends. zigpy announces a new GPD's alias with an aliased Device_annce, and announces it as the coordinator instead when send_packet() raises NotImplementedError (manager.py). Z-Stack's AF data requests have no alias parameters, and zigpy-znp ignores alias_src, so it sends as itself with the alias's fixed TSN 0. Fix: raise NotImplementedError, as zigpy-deconz does.
    4. Housekeeping. Require zigpy>=2.4.0. Against zigpy 2.4.1, 137 of the 167 tests in tests/application currently fail (all pass on 2.3.0). Startup now broadcasts GP Proxy Commissioning Mode, which the fake ZNP server doesn't answer, and later TSNs shift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    Priority

    None yet

    Size

    None yet

    Area

    Radio libraries

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions