Conventions
Getting the object
Test for the factory, create the object once, and keep it in a
variable that lives as long as the driver. An unknown method name
indexes to nil, so individual 4.3.0 methods can be tested
the same way (observed).
local zv2 -- file scope: the driver's only ZigbeeV2 object
function OnDriverLateInit()
if not C4.ZigbeeV2 then return end
zv2 = C4:ZigbeeV2()
if zv2.SendZdoBind then --[[ OS 4.3.0 surface ]] end
end
Inferred, not tested: create it in OnDriverLateInit or
later, the same rule that applies to the older Zigbee calls. A driver
whose device is not a Zigbee node still gets an object, and its calls
fail with "Device not found in Zigbee Manager".
Colon syntax only
Every method checks that its first argument is the object. Called
without one, as zv2.GetSysMeshStatus(), it returns
nothing; called with a plain table in its place, it raises
"An unknown error occurred" (both observed). Assigning a
field on the object raises an error, so keep driver state in your own
tables.
Three ways to fail
Failure modes
| Problem | What happens | Example |
| Missing or mistyped argument |
A Lua error is raised. |
"Not enough arguments passed (expected 1: index)" |
| Value out of range or unsupported |
Returns 0 and a message. Calls that return a
sequence number add a third value: 0 when the
object itself refuses the call, but the sequence number
the refused frame used when the controller refuses it, as
in 0, "Invalid cluster ID provided", 128.
Test only the first value. |
0, "SendZdoGetBindTable invalid index: -1" |
| Refused or lost on the way |
A commandId is returned, then a status
callback carries a non-zero status. |
61447, "ZSTATUS_INCORRECT_GATEWAY_ENDPOINT" |
Type errors name the parameter: "clusterId should be a
number", with " (or nil)" appended for optional
parameters, as in "dstEndpoint should be a number (or
nil)". Wrap calls in pcall when their arguments
come from user input.
Any command or getter can also fail because the controller cannot
reach the device's Zigbee record. A command then returns
0 and a getter nil, with one of
"Device not found in Zigbee Manager",
"Device not found on the mesh",
"Zg server not found for device" or
"ZigbeeManager is null" as the message (inferred).
Argument coercion
How arguments are read
| Input | Result | Seen |
| A numeric string where a number is expected |
Accepted as the number. SendZdoGetBindTable("3")
read from index 3. | observed |
| A fraction |
Truncated. SendZdoBind(6, 0.5) is refused as
endpoint 0. | observed |
| A number where a string is expected |
Converted to text. For an EUI that text is then read as
hex. | observed |
| A value too wide for its field |
Refused where the method or the controller checks it;
otherwise not refused, and the value is cut on the way to
the device, so SendZdoGetBindTable(256) reads
from 0. |
observed for 256; rule inferred |
0 or 1 for a boolean parameter |
Accepted, but both count as true. |
inferred |
| Extra arguments | Ignored. | observed |
EUIs are 16-digit hex strings
Every EUI the API accepts or reports as a string is 16 hex digits,
reported in upper case, for example "0200000000000001".
Pass EUIs the same way. The one exception is
GetSysDeviceInfo().euid, which arrives as a Lua number
and has lost precision. Do not rebuild an EUI string from it (inferred:
an EUI with the top bit set would also come out negative).
commandId is a double
A commandId is a 64-bit value delivered as a Lua number,
so it arrives as a value like 2.4418784162631213e+17 and
is not exact.
The value a command returns and the value its callbacks carry convert
the same way, so comparing them with == works (observed).
Do not format one with %d or do arithmetic on it. A
commandId of 0 always means the command was
refused.
What each result can be matched by
Correlation
| Callback | Carries | Match it by |
| OnZdoSentStatus, OnZclSentStatus,
OnSysCheckInConfigSentStatus, OnSysQueueSentStatus,
OnSysOtaSentStatus |
commandId | The returned
commandId. |
| OnZdoBindResponse, OnZdoUnbindResponse |
nothing | Keep one bind or unbind outstanding at a
time. |
| OnZdoGetBindTableResponse |
startIndex | The index you asked for.
Simplest with one read outstanding. |
| OnZclReceivedEvent |
seqNumber | The seq that
SendZcl returned. |
| OnSysQueueResponse |
cmdId | The commandId that GetSysQueue
returned. |
| OnSysOtaAddImageResponse |
commandId (first argument) |
The commandId that
SendSysOtaAddImage returned. |
| OnZclRead*Response |
the value only | One read per attribute
outstanding. |
Callbacks for different commands interleave. A bind table page was
observed arriving between two status callbacks of the next command.
Binding tables come in pages of three
Each SendZdoGetBindTable answer held at most three
records (observed); inferred, the device sets the page size. entries is the size of the whole table, so keep
asking from startIndex + #binds until that reaches
entries, and stop on an empty page.
Omitted endpoints
For SendZdoBind on OS 4.3.0, omitting
dstEndpoint or passing 0 lets the controller
pick the coordinator endpoint that lists the cluster as a client
cluster (observed). If no coordinator endpoint lists it, the bind is
refused with status 61447 and nothing else. Whether 4.2.1 picks the
endpoint was not established, so on 4.2.1 pass it.
For SendZcl, a Basic read with the endpoint omitted went
out with no destination endpoint and was answered from endpoint 1,
the only endpoint with Basic on that two-endpoint device (observed).
What happens when a cluster sits on several endpoints is not known,
so pass dstEndpoint.
OS 4.2.1 and OS 4.3.0 names
On OS 4.3.0 each 4.2.1 name is a separate Lua function that calls the
same implementation as its new name, and gives identical results
(observed). The refusal texts differ between releases: on 4.2.1 they
begin with the old name, as in "CmdZdoBind invalid clusterId:
", so test the first return value, never the message.
Aliases
| OS 4.2.1 name | OS 4.3.0 name |
| CmdZdoBind | SendZdoBind |
| CmdZdoUnbind | SendZdoUnbind |
| CmdZdoGetBind | SendZdoGetBindTable |
| CmdSetCheckInRes | SetSysCheckInConfig |
| CmdGetCheckInRes | GetSysCheckInConfig |
| CmdZclSetCheckInInterval | SendZclWriteCheckInInterval |
| CmdZclGetCheckInInterval | SendZclReadCheckInInterval |
| CmdZdoExecStatusCb | OnZdoSentStatus |
| CmdZclExecStatusCb | OnZclSentStatus |
| CmdSetCheckInExecStatusCb | OnSysCheckInConfigSentStatus |
| ResZdoBindCb | OnZdoBindResponse |
| ResZdoUnbindCb | OnZdoUnbindResponse |
| ResZdoGetBindCb | OnZdoGetBindTableResponse |
| ResZclGetCheckInIntervalCb | OnZclReadCheckInIntervalResponse |
| CheckInResHandledCb | OnSysCheckInHandledEvent |
Two meanings of bind
In Composer, the connection that ties a Zigbee driver to its device is
also called a binding. Every bind on this page is a ZDO bind instead:
an entry in the device's own binding table that tells the device where
to send reports and commands for one cluster.
What is fixed and what is not
Method names, parameter names, table keys, status names and numbers,
and the message texts are the interface, and are quoted exactly. There
is no helper library around this API. Variable and function names in
the examples, such as zv2, onSent and
readBindTable, are placeholders chosen for this page.
Methods
Called on the object with colon syntax, grouped by what they touch.
The callbacks they lead to are in the next section.
Object and registration
C4:ZigbeeV2
Factory. Observed on OS 4.3.0.
C4:ZigbeeV2() --> object
Returns a new C4LuaZigbeeV2 userdata on every call,
bound to the calling driver's own device. Its
tostring is C4LuaZigbeeV2 (0x...).
Arguments are ignored: C4:ZigbeeV2(12345, "x") still
returns an object for the caller's device.
Side effects. None until Init().
When the object is later garbage collected it removes the device's
callback registration, whichever object made it. See
Behavior notes.
Failure. Inferred: on a driver whose device is not
a Zigbee node the object is still created and its calls fail with
"Device not found in Zigbee Manager".
Availability. OS 4.2.1 and OS 4.3.0.
Unsigned drivers can call it.
Init
Local call. Observed on OS 4.3.0.
zv2:Init() --> 0 | 1
Registers this object as the receiver of the device's callbacks,
replacing any earlier registration for the same device, including
one made by another object.
Returns. 0 on success. Inferred:
1 when the controller's Zigbee manager is unavailable.
Calling it again returns 0 and registers again, which
is how a lost registration is restored.
Without it. Commands and getters work, but no
callback reaches this object. Status and response callbacks for its
commands go to whichever object of the device did call
Init(), and nowhere if none did (observed).
Availability. OS 4.3.0. Present on
OS 4.2.1, not called there.
Binds and the bind table
SendZdoBind, SendZdoUnbind
Command. Observed on OS 4.3.0.
zv2:SendZdoBind(clusterId, srcEndpoint[, dstEndpoint, dstDeviceEuid])
zv2:SendZdoUnbind(clusterId, srcEndpoint[, dstEndpoint, dstDeviceEuid])
--> commandId, message
Asks the device to add, or remove, one entry in its own binding
table: from clusterId on srcEndpoint of
the device to an endpoint on another node, by default the
controller's coordinator.
Parameters
| Name | Type | Notes |
| clusterId | number req |
The cluster. Values below 1 are refused (-1 observed;
0 inferred), so cluster 0x0000 can be neither bound nor
unbound here. No upper
limit is checked; inferred, the value is cut to 16
bits. |
| srcEndpoint | number req |
The device endpoint that hosts the cluster. Values below
1 are refused. |
| dstEndpoint | number or nil |
Endpoint on the destination. Omit it or pass
0 and the controller picks the coordinator
endpoint that lists the cluster. Negative values are
refused. |
| dstDeviceEuid | string or nil |
Destination EUI, 16 hex digits. Omit it or pass
"" for the coordinator. A number is turned
into text and read as hex: 12345 bound to
0000000000012345. A string that is not hex
makes a bind fall back to the coordinator; inferred, an
unbind returns "Invalid destination device
EUID" instead. |
Returns. commandId, "" once the
controller accepts the command. Progress arrives on
OnZdoSentStatus with that commandId, then
the device's answer on OnZdoBindResponse or
OnZdoUnbindResponse, which carry no
commandId.
Failures
| Result | When |
| raises "Not enough arguments passed (expected 2: clusterId, srcEndpoint[, dstEndpoint, dstDeviceEuid])" |
Fewer than two arguments. |
| raises "clusterId should be a number", "srcEndpoint should be a number", "dstEndpoint should be a number (or nil)", "dstDeviceEuid should be a string (or nil)" |
Wrong type. |
| 0, "SendZdoBind invalid clusterId: -1" |
clusterId below 1. |
| 0, "SendZdoBind invalid srcEndpoint: 0" |
srcEndpoint below 1, including 0.5. |
| 0, "SendZdoBind invalid dstEndpoint: -1" |
dstEndpoint below 0. Not observed. |
| status 61447 "ZSTATUS_INCORRECT_GATEWAY_ENDPOINT", then nothing |
The destination endpoint does not list the cluster, or
no coordinator endpoint does. |
SendZdoUnbind refuses with the same
"SendZdoBind" prefix (observed). Inferred: the message
can also be one of the controller's own,
"Device not found in Zigbee Manager",
"Device not found on the mesh",
"Zg server not found for device" or
"ZigbeeManager is null".
Side effects. Changes the device's binding table.
Binding a record that already exists succeeds and adds no duplicate
(observed). The device then sends that cluster's reports and
commands to the destination; attribute reporting itself is set up
separately with ZCL.
Availability. OS 4.3.0. The same
implementation answers to CmdZdoBind and
CmdZdoUnbind, the names present on OS 4.2.1, where the
refusals begin "CmdZdoBind invalid". Unsigned drivers
can call both.
SendZdoGetBindTable
Command. Observed on OS 4.3.0.
zv2:SendZdoGetBindTable(index) --> commandId, message
Reads one page of the device's binding table with a ZDO
Mgmt_Bind_req.
Parameters
| Name | Type | Notes |
| index | number req |
First record to return, counting from 0. A numeric
string works. Negative values are refused. 256 read from
0; inferred, the index is sent as one byte. |
Returns. commandId, "". The page
arrives on OnZdoGetBindTableResponse. An index at or
past the end returns an empty binds with
entries still set to the table size.
Failure. Raises "Not enough arguments passed
(expected 1: index)" or "index should be a
number", which booleans also get. Returns
0, "SendZdoGetBindTable invalid index: -1" for a
negative index.
Availability. OS 4.3.0. Also
answers to CmdZdoGetBind, the OS 4.2.1 name, where the
refusal begins "CmdZdoGetBind invalid index: ".
Bind config
SetZdoBindConfig
Local call. Errors observed, effect not observed.
zv2:SetZdoBindConfig(entries) --> 1, "" | 0, message
-- entries: { { clusterId = n, srcEndpoint = n,
-- dstEndpoint = n, dstDeviceEuid = s }, ... }, or nil
Hands the controller the complete list of unicast binds the device
should hold. Inferred: from then on the controller keeps the
device's binding table matched to the list, adding binds that are
missing and removing every other unicast bind.
Entry fields
| Name | Type | Notes |
| clusterId | number req |
1 or more. Inferred: cut to 16 bits. |
| srcEndpoint | number req |
1 or more. Inferred: cut to 8 bits. |
| dstEndpoint | number or nil |
0 or more. Inferred: 0 or absent lets the controller
pick. |
| dstDeviceEuid | string or nil |
Destination EUI. Inferred: absent means the
coordinator. |
Returns. 1, "" when stored (inferred).
This is not a commandId. Progress arrives on
OnZdoBindConfigStatusEvent (inferred).
Failures
| Result | When |
| 0, "SetZdoBindConfig expects a table or nil argument" |
Any value that is neither. Observed for a boolean and a
string. |
| 0, "SetZdoBindConfig entry 1 is not a table" |
Observed. |
| 0, "SetZdoBindConfig entry 1 missing required field: clusterId" |
Observed. |
| 0, "SetZdoBindConfig entry 1 missing required field: srcEndpoint" |
Observed. |
| 0, "SetZdoBindConfig entry N invalid clusterId: X" |
Below 1. Not observed. |
| 0, "SetZdoBindConfig entry N invalid srcEndpoint: X" |
Below 1. Not observed. |
| 0, "SetZdoBindConfig entry N invalid dstEndpoint: X" |
Below 0. Not observed. |
A refused list changes nothing: the stored config read the same
before and after each refusal (observed).
Side effects. Inferred, never exercised, and not
reversible by the API:
nil, no argument, or an empty table clears the list,
and the controller then unbinds every unicast bind on the device.
The list claims every bind with a 64-bit destination address,
whatever node it points at, so binds made by other tools or between
two devices are removed too. The list is held by the controller per
device: it outlives the object, is sent again when the device comes
back online, and is dropped when the device is removed. Sending an
unchanged list does nothing. Re-apply the list after a controller
restart.
Availability. OS 4.3.0 only.
Unsigned drivers can call it.
GetZdoBindConfig
Getter. Observed on OS 4.3.0.
zv2:GetZdoBindConfig() --> entries, "" | nil, message
Returns the list last stored with SetZdoBindConfig, from
the controller. Inferred: it does not read the device.
Returns. An array of
{ clusterId, srcEndpoint, dstEndpoint, dstDeviceEuid }.
Inferred: dstEndpoint is left out when 0 and
dstDeviceEuid when empty. Observed only as
{}, with nothing stored.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
Sending ZCL and ZDO
SendZcl
Command. Observed on OS 4.3.0.
zv2:SendZcl({ clusterId = n, commandId = n, ... })
--> commandId, message, seq
Sends one ZCL frame to the device. The object builds the ZCL header
from the fields below; payload carries the bytes that
follow it.
Table fields
| Name | Type | Notes |
| clusterId | number req |
0 to 0xFFFF. The controller also refuses ids outside
its cluster list with 0, "Invalid cluster ID
provided", seq (observed for 0xEF00, 0xFF03 and
0xFCC0); see
Clusters SendZcl
accepts. |
| commandId | number req |
The ZCL command identifier, 0 to 0xFF. |
| frameType | string |
"cluster", the default (inferred), or
"global". Any other string, or a number,
is refused. |
| direction | string |
"toServer", the default (inferred), or
"toClient". |
| payload | string |
Bytes after the ZCL header. A value of another type is
ignored. |
| dstEndpoint | number |
Device endpoint, 0 to 0xFE. endpoint is
accepted as another name for it. Default 0. Inferred:
the controller then resolves the endpoint. This was seen
only for a cluster the device hosts on one endpoint.
Pass it explicitly. |
| srcEndpoint | number |
0 to 0xFE. Default 0; inferred, the controller
picks. |
| manufacturerCode | number |
Sets the manufacturer-specific bit and code. |
| disableDefaultResponse | boolean |
Default true. Only a boolean is read;
anything else leaves the default. |
| profileId | number |
Default 0x0104. The controller refuses unknown profiles
(observed for 0xC05E, "Invalid profile ID
provided"). Inferred accepted list: 0x0000,
0x0104, 0x0105, 0x0109, 0xA1E0, 0xC00E and 0xC25C to
0xC25E. |
| apsOptions | number |
Default 0x0100. Inferred: above 0xFFFF is refused by the
controller. |
| addressMode | string |
Only "unicast". |
| groupId, broadcastAddr, targetEuid, priority | absent |
Any value is refused. |
Returns. commandId, "", seq.
seq is the ZCL transaction sequence number the object
put in the header, always 0x80 to 0xFF; the device's answer carries
it back as seqNumber. Status arrives on
OnZclSentStatus. The answer arrives on
OnZclReceivedEvent when that callback is set, and on the
driver's OnZigbeePacketIn when it is not (both
observed).
Failure. A refusal never raises. The object's own
checks (the messages that begin "SendZcl") return
0, message, 0. The controller's checks return
0, message, seq: observed for "Invalid cluster
ID provided" and "Invalid profile ID
provided", inferred for its endpoint and APS checks. Test
the first value only.
Refusals
| Message | When |
| "SendZcl expects a single table argument" |
No argument, or not a table. |
| "SendZcl: required field 'clusterId' missing or not a number" |
Missing, or not a number. |
| "SendZcl: required field 'commandId' missing or not a number" |
Missing, or not a number. |
| "SendZcl: frameType must be 'cluster' or 'global'" |
Any other string, or a number. |
| "SendZcl: direction must be 'toServer' or 'toClient'" |
Any other value. |
| "SendZcl: addressMode 'group' is not supported yet (unicast only)" |
Any addressMode but "unicast";
the value is quoted. |
| "SendZcl: 'groupId' is not supported yet (unicast to bound device only)" |
Also for broadcastAddr,
targetEuid and priority. |
| "SendZcl: invalid clusterId: 70000" |
Above 0xFFFF or negative. |
| "SendZcl: invalid commandId: 256" | Above 0xFF. |
| "SendZcl: invalid endpoint: 255" |
dstEndpoint or endpoint above
0xFE. |
| "SendZcl: invalid srcEndpoint: 255" | Above 0xFE. |
| "Invalid cluster ID provided", "Invalid profile ID provided" |
From the controller. Observed; the third value is the
seq. |
| "Invalid APS options provided" |
From the controller. Inferred, not observed. |
Frame type default. Inferred, never sent (do
not test it on Basic): an omitted frameType means
cluster-specific. A global Read
Attributes (command 0x00) sent without
frameType = "global" becomes command 0x00 of that
cluster instead, which is Reset to Factory Defaults on Basic and Off
on On/Off.
Side effects. Advances the object's sequence
number, even when the controller then refuses the frame (observed),
and adds the commandId to the object's tracked
set.
Availability. OS 4.3.0 only.
Unsigned drivers can call it (observed). A Basic read with
frameType = "global" was observed end to end; the
manufacturerCode, apsOptions,
srcEndpoint and direction = "toClient"
options were not exercised, and profileId only as a
refusal.
SendZdo
Command. Errors observed, effect not observed.
zv2:SendZdo({ clusterId = n, payload = s }) --> commandId, message
Sends a raw ZDO request to the device. Signed drivers and agents
only.
Table fields
| Name | Type | Notes |
| clusterId | number req |
ZDO cluster, 0 to 0xFFFF. |
| payload | string |
ZDO frame bytes. Whether the leading transaction
sequence byte belongs in it is not known. |
| priority | absent |
Refused if present. |
Failure. From an unsigned driver every call, with
any argument or none, returns 0, "SendZdo requires a signed
driver or agent" (observed). Signed callers can also get
"SendZdo expects a single table argument",
"SendZdo: 'priority' is not supported yet",
"SendZdo: required field 'clusterId' missing or not a
number" and "SendZdo: invalid clusterId: N".
Results. Inferred: status on
OnZdoSentStatus. Of the device's ZDO answers, only
Bind_rsp, Unbind_rsp and
Mgmt_Bind_rsp reach Lua, through the bind callbacks.
Any other ZDO response is dropped.
Availability. OS 4.3.0 only. Signed
drivers and agents only.
Poll control and check-in
These calls use the device's Poll Control cluster (0x0020), which
sleepy end devices use to agree how often they wake and poll.
Intervals and timeouts are in quarter seconds, as the ZCL defines
those attributes (inferred for this API). None of these was tested on
a device that has the cluster.
SendZclReadCheckInInterval, SendZclReadLongPollInterval, SendZclReadShortPollInterval, SendZclReadFastPollTimeout
Command. Observed on OS 4.3.0.
zv2:SendZclReadCheckInInterval(dstEndpoint[, srcEndpoint])
zv2:SendZclReadLongPollInterval(dstEndpoint[, srcEndpoint])
zv2:SendZclReadShortPollInterval(dstEndpoint[, srcEndpoint])
zv2:SendZclReadFastPollTimeout(dstEndpoint[, srcEndpoint])
--> commandId, message
Each sends a global Read Attributes for one Poll Control attribute,
with the default response enabled.
Attributes
| Method | Attribute | Value callback |
| SendZclReadCheckInInterval | 0x0000 Check-in Interval, uint32 |
OnZclReadCheckInIntervalResponse |
| SendZclReadLongPollInterval | 0x0001 Long Poll Interval, uint32 |
OnZclReadLongPollIntervalResponse |
| SendZclReadShortPollInterval | 0x0002 Short Poll Interval, uint16 |
OnZclReadShortPollIntervalResponse |
| SendZclReadFastPollTimeout | 0x0003 Fast Poll Timeout, uint16 |
OnZclReadFastPollTimeoutResponse |
Parameters
| Name | Type | Notes |
| dstEndpoint | number req |
Device endpoint that hosts Poll Control. The controller
refuses values above 0xFE. |
| srcEndpoint | number or nil |
Default 0. The controller refuses values above
0xFE. |
Returns. commandId, "": two values,
no sequence number. Status arrives on
OnZclSentStatus.
Failure. Raises "Not enough arguments passed
(expected 1: dstEndpoint[, srcEndpoint])",
"dstEndpoint should be a number" or
"srcEndpoint should be a number (or nil)". Returns
0, "Invalid destination endpoint provided" for a
dstEndpoint of 300 and 0, "Invalid source
endpoint provided" for a srcEndpoint of 300.
Results. On a device without Poll Control each
read reported status 61445, 5, 0, and the device answered with a
Default Response carrying status 0xC3 (unsupported cluster), which
arrived on OnZclReceivedEvent; no value callback fired
(observed). On a device with the cluster the value is expected on
the matching callback above (inferred).
Availability. OS 4.3.0.
SendZclReadCheckInInterval also answers to
CmdZclGetCheckInInterval, present on OS 4.2.1. The other
three are OS 4.3.0 only.
SendZclWriteCheckInInterval
Command. Errors observed, effect not observed.
zv2:SendZclWriteCheckInInterval(interval, dstEndpoint[, srcEndpoint])
--> commandId, message
Writes the Check-in Interval attribute (0x0000, uint32) with a
global Write Attributes.
Parameters
| Name | Type | Notes |
| interval | number req |
Quarter seconds. Negative values are refused. |
| dstEndpoint | number req |
Device endpoint that hosts Poll Control. |
| srcEndpoint | number or nil |
Default 0. |
Returns. commandId, "". Status on
OnZclSentStatus; inferred, the device's Write
Attributes Response arrives as an incoming frame.
Failure. Raises "Not enough arguments passed
(expected 2: interval, dstEndpoint[, srcEndpoint])" or
"interval should be a number" (observed). Returns
0, "Invalid interval: N" for a negative interval.
Availability. OS 4.3.0. Also answers
to CmdZclSetCheckInInterval, present on OS 4.2.1, whose
parameter names there are not known.
SendZclSetLongPollInterval, SendZclSetShortPollInterval
Command. Errors observed, effect not observed.
zv2:SendZclSetLongPollInterval(interval, dstEndpoint[, srcEndpoint])
zv2:SendZclSetShortPollInterval(interval, dstEndpoint[, srcEndpoint])
--> commandId, message, seq
Send the Poll Control commands Set Long Poll Interval (0x02, uint32
payload) and Set Short Poll Interval (0x03, uint16 payload) as
cluster-specific frames, with the default response enabled.
Parameters
| Name | Type | Notes |
| interval | number req |
Quarter seconds. Not range checked; inferred, cut to the
payload width. |
| dstEndpoint | number req |
Device endpoint that hosts Poll Control. |
| srcEndpoint | number or nil |
Default 0. |
Returns. commandId, "", seq. Status on
OnZclSentStatus.
Failure. Raises "Not enough arguments passed
(expected 2: interval, dstEndpoint[, srcEndpoint])",
"interval should be a number" or "dstEndpoint
should be a number" (observed). Inferred: a controller
refusal returns 0, message, seq, as for
SendZcl.
Availability. OS 4.3.0 only.
SendZclFastPollStop
Command. Errors observed, effect not observed.
zv2:SendZclFastPollStop(dstEndpoint[, srcEndpoint]) --> commandId, message, seq
Sends Fast Poll Stop (0x01, no payload), telling the device to leave
fast polling before its timeout.
Returns. commandId, "", seq. Status on
OnZclSentStatus.
Failure. Raises "Not enough arguments passed
(expected 1: dstEndpoint[, srcEndpoint])" or
"dstEndpoint should be a number" (observed). Inferred:
a controller refusal returns 0, message, seq.
Availability. OS 4.3.0 only.
GetSysCheckInConfig
Getter. Observed on OS 4.3.0.
zv2:GetSysCheckInConfig() --> config, "" | nil, message
Returns how the controller answers the device's Poll Control
Check-in commands.
Returns.
{ fastPoll = boolean, timeout = number, doNotSendResponse =
boolean }. Three devices without Poll Control all returned
{ doNotSendResponse = false, fastPoll = true, timeout = 240
}. Inferred meanings:
fastPoll, the Check-in Response tells the device to
start fast polling; timeout, the fast poll timeout sent
with it, in quarter seconds, so 240 is 60 seconds;
doNotSendResponse, the controller does not answer
check-ins itself.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0. Also answers
to CmdGetCheckInRes, present on OS 4.2.1.
SetSysCheckInConfig
Command. Errors observed, effect not observed.
zv2:SetSysCheckInConfig([fastPoll, timeout, doNotSendResponse])
--> commandId, message
Changes how the controller answers this device's check-ins
(inferred).
Parameters
| Name | Type | Notes |
| fastPoll | boolean or nil |
Default true. |
| timeout | number or nil |
Default 32, not the 240 that
GetSysCheckInConfig reported, so
SetSysCheckInConfig(true) changes the
timeout to 32. Negative values are refused. |
| doNotSendResponse | boolean or nil |
Default false. |
No arguments is a write. Every parameter is
optional, so SetSysCheckInConfig() is not a read: it
sends fastPoll = true, timeout = 32 and
doNotSendResponse = false (inferred).
Answering check-ins. Inferred: the controller
answers the device's check-ins itself unless
doNotSendResponse is true. A driver that
also answers through C4:SendZigbeePacket should set it,
or not answer.
Returns. commandId, "". Status on
OnSysCheckInConfigSentStatus.
Failure. Raises "fastPoll should be a boolean
(or nil)" or "timeout should be a number (or
nil)" (observed). Returns 0, "SetSysCheckInConfig
invalid timeout: N" for a negative timeout. Whether
GetSysCheckInConfig then reports the new values was not
checked.
Availability. OS 4.3.0. Also answers
to CmdSetCheckInRes, present on OS 4.2.1, where the
refusal begins "CmdSetCheckInRes invalid timeout: ".
Device state and the queue
GetSysDeviceInfo
Getter. Observed on OS 4.3.0.
zv2:GetSysDeviceInfo() --> info, "" | nil, message
Returns what the controller knows about the device: network
identity, manufacturer and model strings, firmware, and the endpoint
and cluster inventory. The shape is under
Device info.
endpoints is keyed by endpoint number, so iterate it
with pairs. euid is an inexact number.
firmwareVersion is several lines of text.
lastMessageAge stayed the same across reads more than
an hour apart, and did not move on a device that reported every two
seconds, so it
is not a live age (observed).
Failure. Inferred: nil with
"Device info not available" or "Device not found
in Zigbee Manager".
Availability. OS 4.3.0 only.
GetSysDeviceStatus
Getter. Observed on OS 4.3.0.
zv2:GetSysDeviceStatus() --> status, "" | nil, message
Returns. { statusInt = 0, status = "ONLINE"
} (observed) or { statusInt = 1, status = "OFFLINE"
}, with rebootTimestamp added when it is not
zero. Inferred: there are only these two values, and the
controller's unknown, offline and unstable states all report
1, "OFFLINE". rebootTimestamp is probably
in milliseconds.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
GetSysDeviceDiscovery
Getter. Observed on OS 4.3.0.
zv2:GetSysDeviceDiscovery() --> discovery, "" | nil, message
Returns { status, statusInt }: how far the controller
has got interviewing the device after it joined.
statusInt and status
| statusInt | status |
| 0 | NONE |
| 1 | NEW |
| 2 | STACK_REV |
| 4 | ENDPOINTS |
| 8 | CLUSTERS |
| 16 | NEIGHBORS |
| 32 | CHILDREN |
| 64 | DONE |
| 128 | FAILED |
64, "DONE" was observed; the other rows are inferred.
OnSysDeviceDiscoveryEvent numbers the same process
differently.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
GetSysMeshStatus
Getter. Observed on OS 4.3.0.
zv2:GetSysMeshStatus() --> mesh, "" | nil, message
Returns { meshStatus }, the state of the Zigbee
network the device is on: "UNKNOWN", "UP",
"DOWN", "BUSY", "IDLE" or
"OPEN". "UP" was observed; the others are
inferred, and "OPEN" probably means joining is
allowed.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
GetSysQueue
Command. Observed on OS 4.3.0.
zv2:GetSysQueue() --> commandId, message
Asks the controller for this device's queue of outgoing messages.
Despite the name it is asynchronous.
Results. OnSysQueueResponse(report),
then OnSysQueueSentStatus(commandId, 0, "ZSTATUS_OK"),
in that order (observed). The report is under
Queue report.
Failure. 0, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
SetSysQueueEmpty
Command. Signature only: behavior not observed.
zv2:SetSysQueueEmpty() --> commandId, message
Inferred: discards this device's pending outgoing messages in the
controller. It takes no arguments and checks none, so any call
acts.
Results. Status on
OnSysQueueSentStatus. Inferred: refused while the
controller is still setting the device up after it joined.
Failure. 0, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
OTA images
SendSysOtaAddImage
Command. Errors observed, effect not observed.
zv2:SendSysOtaAddImage({ { url = s, sha256 = s }, ... })
--> commandId, message
Registers firmware image files for the controller to offer the
device over the OTA Upgrade cluster (inferred).
Image fields
| Name | Type | Notes |
| url | string req |
Where the controller fetches the image. An unsigned
driver may only name the controller itself:
controller://..., or http://
or https:// with the host
director, 127.0.0.1 or
localhost, followed by the end of the URL
or one of : / ? #. Case is ignored. |
| sha256 | string |
Digest of the image; inferred, as hex. |
Returns. commandId, "". Inferred:
status on OnSysOtaSentStatus, a per-image report on
OnSysOtaAddImageResponse, and upgrade progress on
OnSysOtaProgressEvent.
Failure. Returns 0, "SendSysOtaAddImage:
images[1] must be a table" or 0, "SendSysOtaAddImage:
images[1].url is required" (observed), and
0, "SendSysOtaAddImage: images[N] off-box URL '<url>'
requires a signed driver or agent" for an unsigned driver
naming another host (not observed).
Side effects. Inferred, never exercised:
nil, no argument, a value that is not a table, or an
empty list removes every image registered for the device.
Availability. OS 4.3.0 only.
Off-box URLs need a signed driver or agent.
GetSysOtaInfo
Getter. Observed on OS 4.3.0.
zv2:GetSysOtaInfo() --> ota, "" | nil, message
Returns the device's OTA state and the images registered for it:
{ stateInt, state, currentVersion, progress, images }.
Observed idle, with stateInt = 1,
state = "idle", the device's firmware version and an
empty images. The shape is under
OTA.
Failure. nil, message as under
Three ways to fail
(inferred).
Availability. OS 4.3.0 only.
Callbacks
Each callback is set with the method of the same name and later runs
with the arguments below. Only the object that called
Init() last receives any. The object is never passed to
the callback.
Callback setters
Local call. Observed on OS 4.3.0.
zv2:OnZdoSentStatus(func) --> zv2
zv2:OnZdoSentStatus(onSent):OnZdoBindResponse(onBind) -- setters chain
Every setter, all 22 On* names and the 8 older
*Cb names, stores func and returns the
object, so calls chain.
Failure. Raises "Not enough arguments passed
(expected 1: func)" with no argument, and "func should
be a function" for nil, a number or a
string.
No setter clears a slot, because nil is refused. The
only way to stop callbacks is to drop the object, which also removes
the device's registration. A 4.2.1 name and its 4.3.0 name set the
same slot: the last one set wins, and a function set under the other
name never runs (observed). Inferred: an error raised inside a
callback is caught and logged by the controller, and return values
are ignored.
All callbacks
| Setter | OS 4.2.1 name | Arguments | Seen |
| OnZdoSentStatus | CmdZdoExecStatusCb | commandId, status, statusName | observed |
| OnZclSentStatus | CmdZclExecStatusCb | commandId, status, statusName | observed |
| OnSysCheckInConfigSentStatus | CmdSetCheckInExecStatusCb | commandId, status, statusName | not seen |
| OnZdoBindResponse | ResZdoBindCb | zdoStatus, zdoStatusName | observed |
| OnZdoUnbindResponse | ResZdoUnbindCb | zdoStatus, zdoStatusName | observed once |
| OnZdoGetBindTableResponse | ResZdoGetBindCb | page | observed |
| OnZclReceivedEvent | none | frame | observed |
| OnZclReadCheckInIntervalResponse | ResZclGetCheckInIntervalCb | value | not seen |
| OnZclReadLongPollIntervalResponse | none | value | not seen |
| OnZclReadShortPollIntervalResponse | none | value | not seen |
| OnZclReadFastPollTimeoutResponse | none | value | not seen |
| OnSysCheckInHandledEvent | CheckInResHandledCb | handled | not seen |
| OnSysDeviceStatusChangedEvent | none | status | not seen |
| OnSysDeviceInfoEvent | none | info | not seen |
| OnSysDeviceDiscoveryEvent | none | stateInt, stateName, endpoint | not seen |
| OnSysMeshStatusEvent | none | meshStatus | not seen |
| OnZdoBindConfigStatusEvent | none | bindState | not seen |
| OnSysQueueSentStatus | none | commandId, status, statusName | observed |
| OnSysQueueResponse | none | report | observed |
| OnSysOtaSentStatus | none | commandId, status, statusName | not seen |
| OnSysOtaAddImageResponse | none | commandId, report | not seen |
| OnSysOtaProgressEvent | none | progress | not seen |
Callbacks marked not seen did not fire during ordinary traffic; no
join, leave or check-in was provoked.
OnZdoSentStatus, OnZclSentStatus, OnSysCheckInConfigSentStatus, OnSysQueueSentStatus, OnSysOtaSentStatus
Callback. Observed on OS 4.3.0.
function onSent(commandId, status, statusName) end
Progress of a command, keyed by the commandId it
returned. statusName is the name of
status; see Status
values.
Which commands report where
| Setter | Reports | Seen |
| OnZdoSentStatus |
Bind, unbind and bind table commands from any object of
the device (observed); raw ZDO commands
(inferred). | observed |
| OnZclSentStatus |
SendZcl and the poll control commands
(observed); only those this object issued
(inferred). | observed |
| OnSysCheckInConfigSentStatus |
SetSysCheckInConfig. | not seen |
| OnSysQueueSentStatus |
GetSysQueue,
SetSysQueueEmpty. | observed |
| OnSysOtaSentStatus |
SendSysOtaAddImage. | not seen |
A ZDO or ZCL command normally reports 61445
"ZSTATUS_QUEUE_RECEIVED", then 5
"ZSTATUS_IN_PROGRESS", then 0 "ZSTATUS_OK", in
quick succession (observed). GetSysQueue reported only
0. ZSTATUS_OK means the frame was sent; the
device's answer is a separate callback. Inferred: any status other
than 61445 and 5 is final.
Inferred: OnZclSentStatus fires only for commands this
object issued and still tracks. It tracks at most 256, drops the
oldest first, and stops tracking a command at its final status. Frames sent
with C4:SendZigbeePacket never reach it (observed).
Availability. OS 4.3.0. The first
three also answer to CmdZdoExecStatusCb,
CmdZclExecStatusCb and
CmdSetCheckInExecStatusCb, present on OS 4.2.1.
OnZdoBindResponse, OnZdoUnbindResponse
Callback. Observed on OS 4.3.0.
function onBind(zdoStatus, zdoStatusName) end -- 0, "ZDO_STATUS_SUCCESS"
The device's Bind_rsp or Unbind_rsp.
zdoStatus is the ZDO status number and
zdoStatusName its name; see
ZDO status values.
It carries no commandId, cluster or endpoint, so a
driver can only attribute it by having one request outstanding.
Inferred: it fires for every bind or unbind response the device
sends, whoever sent the request.
Availability. OS 4.3.0. Also
ResZdoBindCb and ResZdoUnbindCb, present on
OS 4.2.1.
OnZdoGetBindTableResponse
Callback. Observed on OS 4.3.0.
function onTable(page) end
One page of the device's binding table, shaped as under
Bind table page.
binds is an array. Inferred: the address mode of each
record is dropped, so a group-addressed record cannot be told apart
from a unicast one.
Availability. OS 4.3.0. Also
ResZdoGetBindCb, present on OS 4.2.1.
OnZclReceivedEvent
Callback. Observed on OS 4.3.0.
function onZcl(frame) end
Each ZCL frame from the device: attribute reports, answers to
SendZcl, and default responses. frame.payload
is the whole ZCL frame, header included. The shape is under
Incoming ZCL frame.
Side effect. Setting it on the registered object
takes those frames away from the driver's
OnZigbeePacketIn. The one exception is a Read
Attributes Response with a sequence number below 0x80, which goes
only to OnZigbeePacketIn. See
Behavior notes.
Availability. OS 4.3.0 only.
OnZclReadCheckInIntervalResponse, OnZclReadLongPollIntervalResponse, OnZclReadShortPollIntervalResponse, OnZclReadFastPollTimeoutResponse
Callback. Shape inferred, not observed.
function onPollValue(value) end
Inferred: the object reads every Poll Control (0x0020) Read
Attributes Response from the device and passes each attribute value
to its slot: 0x0000 and 0x0001 as uint32, 0x0002 and 0x0003 as
uint16. Records of another type, or with a failure status, are
skipped, as are manufacturer-specific responses. These fire as well
as OnZclReceivedEvent, and for any read of those
attributes, however it was sent.
Availability. OS 4.3.0. The first
also answers to ResZclGetCheckInIntervalCb, present on
OS 4.2.1.
OnSysCheckInHandledEvent
Callback. Shape inferred, not observed.
function onCheckIn(handled) end -- always true
Inferred: fires when the controller has answered a Check-in command
from the device, with handled always
true.
Availability. OS 4.3.0. Also
CheckInResHandledCb, present on OS 4.2.1.
OnSysDeviceStatusChangedEvent
Callback. Shape inferred, not observed.
function onDeviceStatus(status) end -- { statusInt, status, rebootTimestamp }
The same table as GetSysDeviceStatus. Inferred: fires
when the device goes online or offline and when it reboots, with
rebootTimestamp present only when it is not zero.
Side effect. Inferred, not tested: while this
callback is set, the controller stops applying the device's online,
offline and reboot changes through the older driver path. Test a
driver that relies on OnZigbeeOnlineStatusChanged
before setting it.
Availability. OS 4.3.0 only.
OnSysDeviceInfoEvent
Callback. Shape inferred, not observed.
function onDeviceInfo(info) end
The same table as GetSysDeviceInfo. Inferred: fires
when the controller's record of the device changes, such as after
the device is interviewed again.
Availability. OS 4.3.0 only.
OnSysDeviceDiscoveryEvent
Callback. Shape inferred, not observed.
function onDiscovery(stateInt, stateName, endpoint) end
Steps of the controller's interview of the device. The numbering is
not the one GetSysDeviceDiscovery uses.
stateInt and stateName
| stateInt | stateName |
| 0 | NEW |
| 1 | NODE_QUEUE |
| 2 | NODE_SEND |
| 3 | NODE_WAITING |
| 4 | ENDPOINTS_QUEUE |
| 5 | ENDPOINTS_SEND |
| 6 | ENDPOINTS_WAITING |
| 7 | SIMPLE_QUEUE |
| 8 | SIMPLE_SEND |
| 9 | SIMPLE_WAITING |
| 10 | LQI_QUEUE |
| 11 | LQI_SEND |
| 12 | LQI_WAITING |
| 13 | IEEE_QUEUE |
| 14 | IEEE_SEND |
| 15 | IEEE_WAITING |
| 16 | JOIN |
| 17 | JOIN_NODE_QUEUE |
| 18 | JOIN_NODE_SEND |
| 19 | JOIN_NODE_WAITING |
| 20 | JOIN_KEY_WAITING |
| 21 | DONE |
| 255 | FAILED |
endpoint is the device endpoint the step concerns
(inferred).
Availability. OS 4.3.0 only.
OnSysMeshStatusEvent
Callback. Shape inferred, not observed.
function onMesh(meshStatus) end -- a string, as in GetSysMeshStatus
A single string, not a table. Inferred: it may never fire on OS
4.3.0. Poll GetSysMeshStatus instead.
Availability. OS 4.3.0 only.
OnZdoBindConfigStatusEvent
Callback. Shape inferred, not observed.
function onBindConfig(bindState) end -- { state = "synced" }
Inferred: reports how far the controller has got matching the
device's binding table to the SetZdoBindConfig list.
state is "synced", "pending",
"disabled" or "unknown";
"disabled" means the device refused ZDO binds and the
controller stopped trying.
Availability. OS 4.3.0 only.
OnSysQueueResponse
Callback. Observed on OS 4.3.0.
function onQueue(report) end
The answer to GetSysQueue, shaped as under
Queue report. It arrived
before the command's OnSysQueueSentStatus.
Availability. OS 4.3.0 only.
OnSysOtaAddImageResponse, OnSysOtaProgressEvent
Callback. Shape inferred, not observed.
function onOtaAdded(commandId, report) end
function onOtaProgress(progress) end
OnSysOtaAddImageResponse reports the result of
SendSysOtaAddImage for each image, with the
commandId first. OnSysOtaProgressEvent
reports the device's upgrade as it moves between states. Both
shapes are under OTA.
Availability. OS 4.3.0 only.
Data shapes
Field names are exact. Values in the literals are composed examples,
shaped like the observed ones. Optional fields are left out of the
table when they have no value, as rebootTimestamp is when
it is zero; empty lists such as binds are still
present.
Status values
Confidence: Observed on OS 4.3.0.
status and statusName in every
*SentStatus callback. The full set has 154 values, all
named ZSTATUS_*; these are the ones a driver
is most likely to meet. The four marked observed were seen; the rest
are inferred from their names.
Status values
| status | statusName | Meaning |
| 61445 | ZSTATUS_QUEUE_RECEIVED | Accepted into the controller's queue. Observed. |
| 5 | ZSTATUS_IN_PROGRESS | Being sent. Observed. |
| 0 | ZSTATUS_OK | Sent. Final. Observed. |
| 61447 | ZSTATUS_INCORRECT_GATEWAY_ENDPOINT | The coordinator endpoint does not model the cluster. Final. Observed. |
| 61448 | ZSTATUS_INCORRECT_DEVICE_ENDPOINT | The device endpoint does not fit the request. |
| 61441 | ZSTATUS_ZSERVER3_DEVICE_QUEUE_FULL | The device's queue is full. |
| 61442 | ZSTATUS_ZSERVER3_DEVICE_SHARED_QUEUE_FULL | A shared queue is full. |
| 61443 | ZSTATUS_ZSERVER3_ZIGBEE_GATEWAY_QUEUE_FULL | The radio's queue is full. |
| 61444 | ZSTATUS_ZSERVER3_DEVICE_OFFLINE | The device is offline. |
| 61446 | ZSTATUS_PARTIAL_SUCCESS | Part of a request succeeded. |
| 7 | ZSTATUS_TIMEOUT | Timed out. |
| 3074 | ZSTATUS_ZIGBEE_DELIVERY_FAILED | The network could not deliver it. |
| 3104 | ZSTATUS_ZIGBEE_NO_APS_ACK | The device did not acknowledge it. |
| 3110 | ZSTATUS_ZIGBEE_SEND_UNICAST_NO_ROUTE | No route to the device. |
ZDO status values
Confidence: Observed on OS 4.3.0.
zdoStatus and zdoStatusName in the bind
callbacks, and statusInt and status in a
bind table page. Only 0 "ZDO_STATUS_SUCCESS" was seen;
the other names are exact but were not produced.
ZDO status values
| Value | Name |
| 0 | ZDO_STATUS_SUCCESS |
| 128 | ZDO_STATUS_INVALID_REQUEST_TYPE |
| 129 | ZDO_STATUS_DEVICE_NOT_FOUND |
| 130 | ZDO_STATUS_INVALID_ENDPOINT |
| 131 | ZDO_STATUS_NOT_ACTIVE |
| 132 | ZDO_STATUS_NOT_SUPPORTED |
| 133 | ZDO_STATUS_TIMEOUT |
| 134 | ZDO_STATUS_NO_MATCH |
| 136 | ZDO_STATUS_NO_ENTRY |
| 137 | ZDO_STATUS_NO_DESCRIPTOR |
| 138 | ZDO_STATUS_INSUFFICIENT_SPACE |
| 139 | ZDO_STATUS_NOT_PERMITTED |
| 140 | ZDO_STATUS_TABLE_FULL |
| 141 | ZDO_STATUS_NOT_AUTHORIZED |
| 142 | ZDO_STATUS_DEVICE_BINDING_TABLE_FULL |
| 143 | ZDO_STATUS_INVALID_INDEX |
| 144 | ZDO_STATUS_FRAME_TOO_LARGE |
| 145 | ZDO_STATUS_BAD_KEY_NEGOTIATION_METHOD |
| 146 | ZDO_STATUS_TEMPORARY_FAILURE |
| 173 | APS_STATUS_SECURITY_FAIL |
| 197 | NWK_STATUS_ALREADY_PRESENT |
| 199 | NWK_STATUS_NWK_TABLE_FULL |
| 200 | NWK_STATUS_UNKNOWN_DEVICE |
| 214 | NWK_STATUS_MISSING_TLV |
| 215 | NWK_STATUS_INVALID_TLV |
Bind table page
Confidence: Observed on OS 4.3.0.
The argument to OnZdoGetBindTableResponse.
{
statusInt = 0,
status = "ZDO_STATUS_SUCCESS",
entries = 4, -- records in the whole table
startIndex = 0, -- table index of binds[1]
binds = { -- an array; at most 3 per page observed
{ clusterId = 6, srcEndpoint = 1, dstEndpoint = 1,
dstDeviceEuid = "0200000000000001" },
{ clusterId = 1794, srcEndpoint = 1, dstEndpoint = 3,
dstDeviceEuid = "0200000000000001" },
{ clusterId = 0, srcEndpoint = 1, dstEndpoint = 1,
dstDeviceEuid = "0200000000000001" }, -- SendZdoUnbind refuses cluster 0
},
}
Device info
Confidence: Observed on OS 4.3.0.
Returned by GetSysDeviceInfo and passed to
OnSysDeviceInfoEvent. The field list is exact and was
seen on three devices; the meanings marked inferred were not
confirmed.
{
euid = 1.4411518807585587e+17, -- the EUI as a number: inexact
networkId = 18344, -- inferred: the 16-bit network address
state = 0,
stateName = "NONE", -- inferred others: JOINED 16, UNRESPONSIVE 17,
-- LEAVE_SENT 32, LEFT 48, UNKNOWN 255
lastMessageAge = 5172, -- unit unknown; not a live age
controllerId = 20, -- inferred: device id of the hosting controller
hardwareVersion = 1,
manufacturer = "<manufacturer name the device reports>",
product = "<model the device reports>",
firmwareVersion = "DeviceFwVersion:\n versionStr: 0.0.12\n version: 12\n",
stackRevision = 22,
capabilities = 142,
capabilitiesName = "ROUTER", -- inferred others: SLEEPY 128, END_DEVICE 140,
-- COORDINATOR 143, TRUST_CENTER 207, else UNKNOWN
endpoints = {
[1] = { -- keyed by endpoint number: iterate with pairs
deviceType = 81,
deviceTypeName = "SMART_PLUG",
profileId = 260,
profileName = "HA",
serverClusters = {
{ id = 0, name = "ZCL_BASIC" },
{ id = 6, name = "ZCL_ON_OFF" },
{ id = 65280, name = "0x0000ff00" }, -- unknown ids come back as hex
},
clientClusters = { { id = 25, name = "ZCL_OTA_BOOTLOAD" } },
},
},
}
Incoming ZCL frame
Confidence: Observed on OS 4.3.0.
The argument to OnZclReceivedEvent. This one is an
On/Off attribute report.
{
profileId = 260,
clusterId = 6,
seqNumber = 5, -- ZCL transaction sequence number, also in payload
apsOptions = 256, -- 256 seen on reports, 352 on answers to SendZcl
srcEndpoint = 1, -- device endpoint
dstEndpoint = 1, -- controller endpoint
payload = "\24\5\10\0\0\16\1",
-- the whole ZCL frame: frame control 0x18, seq 5, command 0x0A (Report
-- Attributes), attribute 0x0000, type 0x10 (boolean), value 1
}
SendZcl request
Confidence: Observed on OS 4.3.0.
A read of two Basic attributes, the case observed end to end.
local id, err, seq = zv2:SendZcl({
clusterId = 0x0000,
commandId = 0x00, -- Read Attributes
frameType = "global", -- required: the default is "cluster" (inferred)
dstEndpoint = 1,
payload = "\4\0\5\0", -- attribute ids 0x0004 and 0x0005, little endian
})
-- id ~= 0: the answer comes back with seqNumber == seq
Queue report
Confidence: Observed on OS 4.3.0.
The argument to OnSysQueueResponse. Only an empty queue
was seen, so the shape of messages entries is
inferred.
{
cmdId = 2.4418e+17, -- the commandId GetSysQueue returned
pendingCount = 0,
inflightCount = 0,
total = 0, -- pendingCount + inflightCount
messages = {
{ cmdId = 2.4418e+17, priority = 0, command = "<text>", inflight = false },
},
}
Bind config entry
Confidence: Shape inferred, not observed.
What SetZdoBindConfig takes and
GetZdoBindConfig returns. Field names are exact.
{
{ clusterId = 0x0006, srcEndpoint = 1 }, -- controller picks the rest
{ clusterId = 0x0702, srcEndpoint = 1, dstEndpoint = 3,
dstDeviceEuid = "0200000000000001" },
}
OTA
Confidence: Shape inferred, not observed.
Only an idle GetSysOtaInfo with no images was seen.
Field names are exact; the value lists are inferred.
-- GetSysOtaInfo()
{
stateInt = 1, -- observed idle; inferred: 0 unknown, 2 downloading,
-- 3 started, 4 completed, 5 failed
state = "idle", -- also "unknown", "downloading", "started",
-- "completed", "failed", "transferring"
currentVersion = "0.0.12", -- the device's firmware version
progress = 0, -- unit not confirmed
images = {
{ url = "controller://...", manufacturerId = 0, imageTypeId = 0, version = 0,
storedFileName = "...", statusStr = "SUCCESS", selected = false },
},
}
-- OnSysOtaAddImageResponse(commandId, report)
{
statusInt = 1, statusStr = "SUCCESS",
images = {
{ url = "controller://...", statusInt = 1, statusStr = "SUCCESS",
manufacturerId = 0, imageTypeId = 0, version = 0, storedFileName = "..." },
},
}
-- statusInt and statusStr: 0 UNKNOWN, 1 SUCCESS, 2 FAILURE, 3 URL_NOT_FOUND,
-- 4 DOWNLOAD_FAILED, 5 INVALID_IMAGE, 6 DUPLICATE_IMAGE_TYPE, 7 ALREADY_EXISTS
-- OnSysOtaProgressEvent(progress)
{ state = "downloading", stateInt = 2, progress = 0, manufacturerId = 0,
imageTypeId = 0,
firmwareVersion = 12, -- a number (the version integer), not the text
filename = "..." }
Clusters SendZcl accepts
Confidence: Observed on OS 4.3.0.
The controller accepts only the 117 cluster ids below and refuses the
rest with 0, "Invalid cluster ID provided", seq. Refusal
was observed for 0xEF00, 0xFF03 and 0xFCC0; the accepted list was not
exercised id by id. A cluster the device reports in
GetSysDeviceInfo can still be refused: 0xFF03 was.
Inferred from the list: 0xFF01, 0x000C and 0x0012 are refused too.
Accepted cluster ids
| Range | Ids |
| 0x0000 to 0x00FF | 0x0000-0x000B, 0x000F, 0x0015-0x0016, 0x0019-0x001B, 0x0020-0x0021, 0x0025 |
| 0x0100 to 0x03FF | 0x0100-0x0103, 0x0200-0x0204, 0x0300-0x0301 |
| 0x0400 to 0x06FF | 0x0400-0x0406, 0x040C-0x0429, 0x0500-0x0502, 0x0600-0x0601, 0x0614-0x0615 |
| 0x0700 to 0x0BFF | 0x0700-0x070B, 0x0800, 0x0900-0x0905, 0x0A01-0x0A02, 0x0B00-0x0B05 |
| 0x1000 and up | 0x1000, 0xFC00-0xFC0B, 0xFC57 |