feat: Add Endpoint.sendRaw for non-ZCL APS payloads - #1843
Conversation
Some vendors carry proprietary protocols in bare APS payloads that are not ZCL frames (Control4 keypads carry ASCII text on custom profile 0xC25C). The regular command/write paths always serialize a ZCL header, so they cannot produce such payloads. sendRaw sends the buffer verbatim on the given cluster, typically combined with options.profileId to address a custom profile, riding the normal request pipeline via a minimal frame-shaped carrier whose toBuffer() returns the data as provided. Co-Authored-By: Claude <noreply@anthropic.com>
|
That kind of custom protocol addition was denied introduction before. They just bring too much trouble from maintenance to proper support. That "rougher shape" might work for these devices, but what if next custom protocol needs a slightly different logic... another custom function..? |
|
I read through the concerns in #1792, where vendor protocol logic ended up inside ZH, and am trying to thread the needle here: keep ZH clean while adding a small generic escape hatch. The intent here is to NOT put vendor protocol logic inside ZH - quite the opposite. Sorry if I didn't frame it well. If a future custom protocol needs different logic, that logic lives in its converter and doesn't require changes or special knowledge in ZH. In that sense, this is an attempt to support the devices behind #1792 and similar efforts in a ZH-friendly, spec-friendly way. It is also the TX half of something ZH already does on RX: since #1418 and #1837 the adapters deliberately accept non-ZCL frames on whitelisted custom profiles and deliver them to consumers. Today those consumers can listen but cannot speak without forking core. Happy to rework this in any way you'd like if you can suggest a better approach - including constraining it (for example refusing the standard HA/ZGP profile ids, or documenting it as an expert API with no support guarantees) if the worry is the support burden. I'm just hoping to make this accessible to folks on these non-standard platforms without them having to carry a fork, or putting more burden on the ZH team. |
That behavior is definitely specific to this device. That's the whole problem of custom protocols, each will have their own (even if it's just one bit that needs flipping). Not to mention, potential internal updates to custom protocols would require reworking every aspect to support that as well... Then we'd also end up with issues in specific adapters, because one drops these frames, another cannot process them and crashes, etc.. A custom profile that follows the protocol is very different from an entirely custom protocol. |
Adds
Endpoint.sendRaw(clusterId, data, options), which sends a bare APS payload to the endpoint exactly as provided, with no ZCL framing. Some vendors carry proprietary protocols in APS payloads that are not ZCL frames (Control4 in-wall keypads carry ASCII text on custom profile0xC25C), and every existing send path serializes a ZCL header, so such payloads currently cannot be produced at all. TheprofileIdoption and the adapter plumbing for it already exist; the mandatory ZCL framing is the only gap this closes.The payload rides the normal request pipeline (request queue, sendPolicy, recovery) via a minimal frame-shaped carrier whose
toBuffer()returns the data verbatim. No response correlation is performed anddisableResponsedefaults to true accordingly: replies, if any, arrive through the normal incoming message path, which #1837 already admits for whitelisted custom profiles. If you would prefer a first-class raw-frame type or an adapter-level API over the carrier object, I am happy to rework it to whatever shape you want; this is the minimal version.Tests assert the payload reaches the adapter byte-for-byte with the requested profileId and the disableResponse default, plus the error path.
In production this has driven 31 Control4 devices on an ember coordinator since early July: every LED command, config write, and sequence-matched configuration read goes through this path.
This completes the sequence started in #1837 and #1840, and is the capability ArcadeMachinist asked about when closing #1792 (constructing APS frames from a converter): with it, Control4 support can live entirely in converters with no forked core.