CoAP Observe Trigger Node
Start a pipeline each time an observed CoAP resource changes, with its decoded value, response code and Observe sequence number, without polling.
- Starts on
- Each notification of an observed resource
- Function
- Observe
- Output
- One event per notification
- Dedup
- A re-registration does not repeat the last value
Overview
The CoAP Observe Trigger Node automatically initiates MaestroHub pipelines whenever an observed resource on a CoAP device or gateway sends a notification. Unlike the CoAP GET node, which reads a resource inside an already-running pipeline, the CoAP Observe Trigger starts a new pipeline execution for each notification, enabling fully event-driven automation without polling.
CoAP Observe (RFC 7641) turns a resource into a subscription: MaestroHub registers as an observer once, and the device sends a notification with the current value at registration and then on every change. Each notification carries its response code, its payload in a declared Content-Format, and an Observe sequence number.
Core Functionality
What It Does
1. Event-Driven Pipeline Execution Start pipelines automatically on every notification of the resource the Observe function names. Latency is set by the device, not by any polling interval.
2. Content-Format Aware Payloads
The notification's payload is decoded by the Content-Format the device declared: text as a string, JSON and CBOR as a value, anything else (or no declared format) as base64, with valueEncoding saying which. A payload that does not parse as its declared format is reported as a failure, never guessed. See Content-Format: how payloads are read.
3. Automatic Subscription Lifecycle The observation is registered when the pipeline becomes enabled and cancelled when it's disabled. While it runs, it is registered again by itself whenever it is lost.
4. No Repeat on Re-Registration Every notification carries the time MaestroHub received it and its Observe sequence number, so two notifications of the same value are two distinct events, and each one starts a run. The one exception: when MaestroHub registers again (after a reconnect or an overdue notification), the device answers with its current value, and that value is not emitted again if it is the one already delivered.
How CoAP Observe Triggering Works
CoAP Observe triggering is fundamentally different from on-demand reads:
| Aspect | GET | Observe (Trigger) |
|---|---|---|
| Model | Request-response (pull) | Event-driven (push) |
| Execution | One request per call | One event per notification |
| Lifecycle | Stateless | An observation registered on the device, kept alive by MaestroHub |
| Use Case | On-demand reads, resources that cannot be observed | Real-time change detection, telemetry |
When you configure a CoAP Observe Trigger:
- MaestroHub sends a GET with the Observe option for the function's resource path, from the connection's own UDP socket.
- The device answers with the current value, which is the first event, and registers the observer. A path that does not exist or is refused fails the subscription with the device's code, and the subscription is retried. A resource that answers without the Observe option is not observable, and the subscription fails with that reason.
- The device sends a notification to the same socket on every change. MaestroHub never listens on a known port: notifications return to the ephemeral port the registration came from.
- Each notification that decodes is one trigger event, and the pipeline executes with it available as
$trigger.
Reconnection Handling
MaestroHub keeps the observation alive on its own.
Automatic Recovery
The observation is registered again when:
- The connection is lost: the socket is replaced on reconnect, and the observation is registered on the new one.
- The device ends it: an error notification, or a final notification without the Observe option.
- A notification is overdue: nothing has arrived for the last notification's Max-Age (60 seconds when the device sends none) plus 10 seconds. RFC 7641 lets a client assume the observation was lost at that point; a device that restarted has forgotten its observers.
While the device stays away, registration is retried with backoff from 0.5 seconds up to 30 seconds.
When the observation cannot be kept (the link stays lost, a re-registration is refused or unanswered, the device ends it with an error code, or a notification does not parse as its Content-Format), the trigger fires once per outage with quality bad and the cause under error (see Output Data Structure). The first good notification afterwards is the recovery event, with quality good, delivered even when the value has not changed. A reconnect that restores the observation within half a second raises no event at all.
What This Means for Your Workflows
| Scenario | Behavior |
|---|---|
| Transient network error | One bad event for the outage; a good event when the observation is back |
| Device restart | The device forgets its observers; MaestroHub registers again once the notification is overdue |
| Quiet resource | Registered again about every Max-Age + 10 seconds; an unchanged value starts no run |
| Edited Observe function | Subscribing again replaces the observation, so the new path takes effect |
| MaestroHub restart | All triggers for enabled pipelines are restored on startup |
Changes the device makes while the link is down are not notified. The re-registration after the outage delivers the resource's current value, so the pipeline catches up on state, but not on the intermediate changes.
Configuration Options
Basic Information
| Field | Type | Description |
|---|---|---|
| Node Label | String (Required) | Display name for the node on the pipeline canvas. Must be non-empty (trimmed). |
| Description | String (Optional) | Explains what this trigger initiates. |
Configuration
The trigger configuration is organized across two tabs in the UI: Parameters and Settings.
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
| Connection | Connection ID | "" | Yes | The CoAP connection profile to observe through. Filtered to CoAP connections only. |
| Observe Function | Function ID | "" | Yes | The function that names the resource to observe. Filtered to coap.observe function types on the selected connection. |
| Enabled | Boolean | true | No | When disabled, the subscription is not created even if the pipeline is enabled. |
The selected function must be an Observe function type (coap.observe). GET, PUT, POST and DELETE functions cannot be used with CoAP Observe Trigger nodes.
If the selected connection has no Observe functions yet, the node configuration panel surfaces a link straight to the connection so you can author one.
Observe Function Configuration
The selected function (authored in the Connect module) controls which resource fires the trigger:
| Setting | Description |
|---|---|
| Resource Path | Path of the observable resource, e.g. /sensors/temp |
| Query | Optional query sent with the registration, e.g. unit=C |
| Accept | Ask the device for this Content-Format in its notifications. Empty = the device's default. |
See Observe for the full function reference.
Sample Payload
The Parameters tab also holds a Sample Payload editor and a Sample Quality picker. When you run the node with Test Node in Sandbox mode, the trigger emits the sample payload as result instead of waiting for a real notification, so you can validate downstream nodes before a device is wired up. When the editor is empty, a built-in sample notification is used. Sample Quality sets the quality the sample reports in $trigger._metadata.quality, so a quality-aware pipeline can be tested with a good, uncertain or bad sample. The live subscription and the node's Fire Trigger action do not use the sample.
{
"path": "/sensors/temp",
"code": "2.05",
"status": "Content",
"contentFormat": "application/json",
"value": { "t": 21.5 },
"valueEncoding": "json",
"bytes": 10,
"observe": 17,
"etag": "a1b2",
"maxAge": 30,
"serverTimestamp": "2026-10-20T10:14:03.240Z"
}
Settings
Description
A free-text area for documenting the node's purpose and behavior. Notes entered here are saved with the pipeline and visible to all team members.
Execution Settings
| Setting | Options | Default | Description |
|---|---|---|---|
| On Error | Pipeline Default / Stop Pipeline / Continue Execution | Pipeline Default | Behavior when the node fails. |
Output Data Structure
When the device sends a notification and triggers pipeline execution, the trigger produces a structured output with two top-level keys: _metadata and result. One event is emitted per notification.
Output Format
{
"_metadata": {
"type": "coap_trigger",
"protocol": "coap",
"connectionId": "bf29be94-fc0a-4dc4-8e5c-092f1b74eb4b",
"functionId": "aef374c3-aa2b-454e-aabc-5657faac5950",
"path": "/sensors/temp",
"code": "2.05",
"contentFormat": "application/json",
"observe": "17",
"serverTimestamp": "2026-10-20T10:14:03.240Z",
"quality": "good",
"timestamp": "2026-10-20T10:14:03.240123456Z"
},
"result": {
"path": "/sensors/temp",
"code": "2.05",
"status": "Content",
"contentFormat": "application/json",
"value": { "t": 21.5 },
"valueEncoding": "json",
"bytes": 10,
"observe": 17,
"etag": "a1b2",
"maxAge": 30,
"serverTimestamp": "2026-10-20T10:14:03.240Z"
}
}
result Fields
Keys that only some notifications carry are absent when the notification did not carry them, never filled with a placeholder.
| Field | Type | Description |
|---|---|---|
path | string | The observed resource path. |
code | string | The notification's response code as class.detail, e.g. 2.05. |
status | string | The response code's name, e.g. Content. |
contentFormat | string | The Content-Format the device declared. Present only when the notification carried a Content-Format option. |
value | any | The payload: text as a string, JSON and CBOR as a value, anything else (or no declared format) as base64. Present only when the notification carried a payload. |
valueEncoding | string | text, json or base64: how value represents the payload. Present with value. |
bytes | number | The payload's size on the wire, in bytes. |
observe | number | The Observe sequence number. Present on every notification but a final one, which ends the observation. |
etag | string | The ETag option, as hex. Present when the notification carried one. |
maxAge | number | The Max-Age option: how many seconds the value stays fresh. Present when the device sent one other than the 60-second default. |
serverTimestamp | string | When MaestroHub received the notification, UTC. CoAP carries no device time, so this is the only time. |
_metadata Fields
Every _metadata value is a string, including the numeric ones.
| Field | Type | Description |
|---|---|---|
type | string | Always "coap_trigger". |
protocol | string | Always "coap". |
connectionId | string | The CoAP connection profile ID. |
functionId | string | The Observe function ID. |
path | string | The observed resource path. |
code | string | The notification's response code, e.g. "2.05". |
contentFormat | string | The declared Content-Format. Present only when the device declared one. |
observe | string | The Observe sequence number, as a string ("17"). Present on every notification but a final one. |
serverTimestamp | string | ISO 8601 / RFC 3339, UTC; when MaestroHub received the notification. |
quality | string | good on a notification that arrived and decoded: CoAP itself carries no quality. bad on a failure event: once per outage, when the observation cannot be kept, the trigger fires with quality: "bad" and the cause under error. Its result is the last notification this trigger delivered (an empty object if none had arrived yet). The next good notification is the recovery event. A UNS Publish node downstream inherits the verdict. |
quality_reason | string | Only on a failure event: comm_error. A UNS Publish node downstream forwards it, so the stored sample reads bad · comm_error rather than just bad. |
error | string | Only on a failure event: why the observation was lost, e.g. connection to the CoAP server was lost, or the device's error code. |
timestamp | string | ISO 8601 / RFC 3339 (nanoseconds); when the trigger event was created, UTC. |
Referencing in Downstream Nodes
Use expressions to access trigger data in subsequent nodes:
$trigger.result.value: the decoded payload$trigger.result.valueEncoding:text,jsonorbase64$trigger.result.contentFormat: the declared Content-Format$trigger.result.code: the response code, e.g.2.05$trigger.result.status: the response code's name$trigger.result.path: the observed resource path$trigger.result.bytes: the payload's size$trigger.result.observe: the Observe sequence number, as a number$trigger.result.etag: the ETag, as hex$trigger.result.maxAge: seconds the value stays fresh$trigger.result.serverTimestamp: when MaestroHub received it$trigger._metadata.path: the path as a string, handy for routing on a topic path$trigger._metadata.quality:good, orbadon the once-per-outage failure event$trigger._metadata.connectionId: connection profile used$trigger._metadata.functionId: Observe function used
Validation Rules
Parameter Validation
Node Label
- Must not be empty
- Must not consist only of whitespace
Connection ID
- Must be provided and non-empty
- Must reference a valid CoAP connection profile
Function ID
- Must be provided and non-empty
- Must reference a valid Observe (
coap.observe) function - Function must belong to the specified connection
Enabled Flag
- Must be a boolean if provided
Usage Examples
Sensor Telemetry to the UNS
Key configuration
- Label: Room Temperature
- Connection: Building A Gateway
- Function: Observe on
/sensors/temp, Acceptapplication/json - Enabled: true
- Settings: on error
continue
Downstream usage: $trigger.result.value for the reading, $trigger._metadata.path to build the topic path, $trigger._metadata.quality for the quality band. An outage arrives as one bad event that the UNS stores with its quality.
Alarm Resource Monitoring
Key configuration
- Label: Pump Alarms
- Connection: Pump Station Controller
- Function: Observe on
/alarms/active - Enabled: true
- Settings: on error
stop
Downstream usage: $trigger.result.value for the alarm document, $trigger.result.serverTimestamp for when it arrived. Check $trigger._metadata.quality first, so a lost observation is not read as "no alarms".
Binary Payloads
Key configuration
- Label: Vibration Frames
- Connection: Sensor Node 12
- Function: Observe on
/vibration/frame - Enabled: true
Downstream usage: a device that declares application/octet-stream, or no Content-Format, delivers $trigger.result.value as base64 with $trigger.result.valueEncoding base64; decode it in a JavaScript node.
Best Practices
Subscription Design
- Observe what changes, poll what does not. A resource that is not observable fails the subscription; read it with a CoAP GET on a Schedule trigger instead.
- Set Accept when a device can answer in more than one Content-Format, so
valuealways has the same shape. - One resource per Observe function. Split unrelated resources into separate functions and pipelines.
Designing for Reliability
| Practice | Rationale |
|---|---|
Check quality before acting on a value | bad means the observation was lost, or a notification did not decode (the failure event) |
Branch on valueEncoding, not on the value's type | It always says how value represents the payload |
| Expect a re-registration about every Max-Age + 10 seconds on a quiet resource | It keeps the observation alive across a device restart; an unchanged value starts no run |
Error Handling Strategies
For Critical Workflows:
- Set On Error to
stopPipeline - Monitor pipeline execution failures via the Health dashboard
- Implement alerting for stopped pipelines
For Best-Effort Processing:
- Set On Error to
continueExecution - Log failed executions for later analysis
- Ensure downstream nodes handle partial failures gracefully
Performance Considerations
| Scenario | Recommendation |
|---|---|
| Fast-changing resources | Every notification starts a run; ask the device for a minimum notification interval if it supports one (often a query such as pmin=5) |
| Many observed resources | One connection carries every observation of a device on its one socket; spread unrelated devices across connections |
Enable vs. Disable
- Use the trigger's Enabled parameter to temporarily pause monitoring without changing pipeline state
- Disable triggers during device maintenance to avoid the failure and recovery events a restart produces
- Document the reason for disabled triggers in the node's Description field