apis-saltans-coordinator
High-level Zigbee coordinator API built on top of apis-saltans-hw.
This crate starts the coordinator-side transport actors and exposes small traits for Zigbee operations. It no longer owns device discovery state or binding policy. Applications receive network events, decide what discovery and binding work they need, and call the provided traits to perform the individual ZDP/ZCL operations.
For the internal actor graph and message routing details, see ARCHITECTURE.md.
What You Get
Public API exports:
- coordinator handle:
Coordinator
- hardware driver trait:
Driver(re-exported fromapis-saltans-hw)
- low-level transport traits:
ZclZdp
- deferred response futures:
TransmissionResponseCommunicationResponse<T, U>ZclResponse<T>ZdpResponse<T>
- composed ZDP traits:
NodeEndpointsBinding
- cluster traits:
OnOffColorControlLevelAttributes
- joining control:
Joining
- hardware/NCP helper traits:
AddressTranslationLocalNodeRoutingScanning
- attribute helper aliases:
ReadAttributeResult<T>WriteAttributeResult
- scan result types:
FoundNetworkScannedChannel
- event types:
EventNetworkNetworkErrorDevice
- error type:
Error
Commands without a protocol response return TransmissionResponse, which adapts the hardware
response to the coordinator's Error type.
The Driver re-export lets integration crates import the coordinator API and implement the NCP
driver contract from one dependency. Hardware-specific event translation and startup wiring remain
the backend's responsibility.
Coordinator Lifecycle
Coordinator::start(...) is synchronous and starts three internal tasks:
- the ZCL transceiver
- the ZDP transceiver
- the hardware-event mux
It takes:
- an
NcpHandlefor a running hardware driver actor - a receiver for translated hardware
zb_hw::Eventvalues - a sender for outbound coordinator
Eventvalues
The NCP driver must implement zb_hw::Driver::get_endpoints() and return a complete
zb_zdp::SimpleDescriptor for every local application endpoint. The coordinator retrieves these
descriptors through zb_hw::Ncp::get_endpoints() when it needs them; endpoint descriptors are no
longer passed to Coordinator::start(...).
use ;
use ;
use NcpHandle;
When a remote device sends MatchDescReq, the ZDP transceiver asks the NCP for its current endpoint
descriptors and builds MatchDescRsp from matching descriptors. If the NCP cannot provide them, the
request cannot be answered.
The crate does not persist a device table. Store the FullAddress values received in
Event::Device if your application needs a device registry. The AddressTranslation trait can ask
the NCP to resolve addresses, but persistence and cache policy remain application-owned.
Trait-Based API
The API is intentionally trait-based. Import the traits you use so extension methods are available
on Coordinator.
use ;
The Coordinator implements Zcl, Zdp, Joining, AddressTranslation, LocalNode, Routing,
and Scanning directly. Discovery, binding, cluster, and attribute traits are blanket
implementations over the raw ZCL/ZDP traits, so they are available on the coordinator without a
separate manager object.
Deferred Responses
Sending is split into two observable stages. The first await queues work on the coordinator actor and returns a response future. Awaiting that returned future observes the hardware and protocol result:
TransmissionResponsewaits for hardware completion of a ZCL command that has no application-level response and converts hardware failures intoError::Hardware.ZclResponse<T>waits for hardware completion, then a correlated ZCL frame, and converts that frame toT.ZdpResponse<T>does the same for a correlated ZDP command.CommunicationResponse<Raw, T>is the generic future behind the two protocol aliases.
Consequently, raw transport and command-helper calls commonly use two awaits:
let transmission = api.on.await?;
transmission.await?;
let response = api.communicate.await?;
let typed_response = response.await?;
The first error reports that the request could not be queued or handed off. The second await reports hardware transmission, response-channel, or typed-conversion failures. Dropping a returned response future stops driving and observing that future. The coordinator does not promise that dropping it cancels work already handed to the hardware backend.
Error implements std::error::Error. Hardware, one-shot receive, and timeout variants retain and
expose their source errors and can be constructed through From; the send variant intentionally
discards the failed channel payload.
Higher-level discovery and binding helpers consume both stages internally when they return a final
value. Groups::list(...) and Attributes::configure_reporting(...) intentionally expose a
ZclResponse<T> so callers retain control over when to await the device response.
Events
The application supplies the event channel when starting the coordinator. Events are pushed to that channel directly; there is no subscription API or internal network-manager fan-out.
use ;
async
Event::Zcl is emitted only for inbound frames that do not match an outstanding request.
Request/response traffic is consumed by the relevant communicate(...) call.
An APS packet with cluster ID 0x0025 (Cluster::KeepAlive) under a supported application profile
is handled before ZCL payload decoding and produces Device::KeepAlive. The contained
zb_core::destination::Device identifies the sender by its NWK short address and APS source
endpoint. Packets whose source is not an allocated device short address or whose source endpoint is
reserved are logged and dropped instead of producing an event.
Joining Control
Joining opens the network for joins through the hardware stack.
use Duration;
use Joining;
async
The return value is the effective duration accepted by the hardware.
Hardware Helpers
These traits expose NCP operations that are useful when building application-owned coordinator services.
Local Node
use LocalNode;
use IeeeAddress;
async
LocalNode::get_endpoints() returns the same boxed slice of SimpleDescriptor values supplied by
the NCP. This makes the hardware's endpoint configuration available without maintaining a second
coordinator-owned copy.
Address Translation
use AddressTranslation;
use IeeeAddress;
async
Use this to consult the NCP's address table. Applications should still decide whether and how to cache the result.
Scanning
use ;
async
scan_networks(...) returns discovered networks. scan_channels(...) returns channel scan
observations.
Routing
use Routing;
async
Discovery Building Blocks
Discovery is application-owned. The coordinator provides reusable operations for the standard ZDP steps, and your application chooses when to run them, how to retry them, and what state to persist.
Node Descriptor
use Node;
use Device;
async
Active Endpoints and Simple Descriptors
use Endpoints;
use BTreeMap;
use Endpoint;
use Device;
use SimpleDescriptor;
async
descriptor(...) returns Ok(None) when the response is successful but contains no descriptor.
Non-success ZDP statuses are returned as Error::Zdp(...).
descriptors(...) first calls endpoints(...). If active endpoint discovery fails, the outer
Result is Err(...). If endpoint discovery succeeds, the returned map contains one descriptor
result per endpoint, so callers can keep partial results from endpoints that succeeded.
Binding
Binding sends ZDP BindReq commands. The crate does not decide which clusters should be bound or
when a device is fully integrated.
use Binding;
use ;
use Destination;
async
Use bind_all_to_self(...) when remote endpoint output clusters should be bound to matching local
coordinator endpoints. The helper reads the coordinator IEEE address and local simple descriptors
through LocalNode, intersects each descriptor's input clusters with each remote endpoint's output
clusters, and sends bind requests for matching clusters only.
use ;
use Binding;
use ;
async
The outer Result reports local coordinator lookup failures. The returned map contains per-source
endpoint bind results for requests that were attempted. If multiple local endpoints can receive
clusters from the same remote source endpoint, later local endpoint results overwrite earlier
results for that source endpoint in the returned map.
Use bind_all(...) when you already know the exact ZDP binding destination and want to bind an
endpoint-to-clusters map to that destination.
ZCL Cluster Helpers
Cluster helper traits build standard ZCL commands and send them through the Zcl transport.
Commands that do not expect an application-level response use transmit(...).
On/Off
use OnOff;
use Device as DeviceDestination;
use Device;
use ;
async
The OnOff trait provides on, off, off_with_effect, and toggle.
Level
Level provides the standard level-control commands:
move_to_levelmovestepstopmove_to_level_with_on_offmove_with_on_offstep_with_on_offstop_with_on_offmove_to_closest_frequency
Color Control
use ColorControl;
use Device as DeviceDestination;
use Device;
use ;
use ;
use Options;
async
ColorControl provides move_to_xy and move_to_color_temperature.
Generic Attribute Access
Attributes provides typed ZCL global attribute operations.
The target is a zb_core::destination::Device, which contains the short address and endpoint.
Build or look this up from your own discovery state before calling the trait.
Reads
use ;
use Device as DeviceDestination;
use Device;
use Application;
use Id as BasicReadableId;
async
Writes
use Attributes;
use Device as DeviceDestination;
use Device;
use String;
use Application;
use Attribute as BasicWritable;
async
Reporting
Use configure_reporting(...) with generated ZCL Reportable values. The ZCL attribute value
supplies cluster/profile/manufacturer and data type metadata; the coordinator only transports the
request.
Raw Transports
Use Zcl::transmit(...) for native cluster commands that do not expect an application-level
response. Its first await returns a TransmissionResponse; await that value to confirm hardware
completion. It wraps the opaque zb_hw::HwResponse, so coordinator users receive the local Error
type without depending on the driver's completion mechanism.
Use Zcl::communicate(...) for commands implementing ExpectResponse<zb_zcl::Cluster>. Its first
await returns ZclResponse<T::Response>. Awaiting that response confirms transmission, waits for a
correlated ZCL frame, and converts the frame to the declared response type.
Use Zdp::communicate(...) for ZDP requests implementing ExpectResponse<zb_zdp::Command>. It
returns the equivalent ZdpResponse<T::Response>. The composed traits above are thin wrappers over
these raw transports; most of them await the deferred response internally.
Error Model
Most APIs return apis_saltans_coordinator::Error:
Hardware(zb_hw::Error)SendErrorReceiveError(RecvError)Timeout(Elapsed)InvalidResponseType(String)UnknownDevice(IeeeAddress)InvalidApplicationEndpoint(u8)DurationOutOfBounds(Duration)Zcl(Result<zb_zcl::Status, u8>)Zdp(Result<zb_zdp::Status, u8>)
ZCL and ZDP status responses preserve known status enums and raw unknown status bytes.
For deferred operations, an error can occur at either await boundary. Queue and actor handoff errors occur while obtaining the response future. Hardware, receive-channel, and conversion errors occur while awaiting that response future.
Runtime Configuration
Behavior is configurable through environment variables:
ZIGBEE_COORDINATOR_MPSC_CHANNEL_SIZE
Deferred response futures do not impose a deadline. Applications that require one can wrap the
second await with tokio::time::timeout and select a timeout policy appropriate to the operation.
Retry behavior for discovery or binding is intentionally not configured here anymore. Applications that build discovery or binding workflows should apply their own retry and persistence policy.