Status¶
Status is the current observable state of a device or function right now.
Adapters do not publish status messages directly. Adapter code changes status through generated function methods, framework-managed functions, command helpers, or StatusBatch during polling. The framework turns those state changes into status messages.
What is Status?¶
Question: "What is the current state?"
|
v
+------------------+ +------------------+ +------------------+
| PQ System | | Framework | | Physical Device |
+------------------+ +------------------+ +------------------+
^
|
Adapter reports facts:
- event-driven function call
- poll snapshot via StatusBatch
|
v
+------------------+
| Current Status |
| "Door: open" |
| "Power: fault" |
+------------------+
Status is:
- Present tense - describes state right now
- Snapshot - captured at specific moment
- Changeable - same device may report different status later
- Queryable - PQ can ask "what's your current status?"
Status vs Event vs Command¶
| I want to... | Use |
|---|---|
| Report current state | Status |
| Report something that happened | Event |
| Request an action | Command |
STATUS: "Door is unlocked" (right now)
EVENT: "Door was unlocked" (in the past)
COMMAND: "Unlock the door" (in the future)
Status vs Event: Key Difference¶
Timeline: ----[unlock cmd]----[door opens]----[door closes]----
| | |
Events: | "door.opened" "door.closed"
| | |
Status: "locked" "unlocked" "locked"
"closed" "open" "closed"
Events mark points in time (something happened). Status describes continuous state (current condition).
Common Status Types¶
Connection Status¶
+------------+ +------------+ +------------+
| offline | --> | connecting| --> | online |
+------------+ +------------+ +------------+
^ |
+-------------------------------------+
(connection lost)
| Status | Meaning |
|---|---|
online |
Device connected and responding |
offline |
Device not reachable |
connecting |
Attempting to connect |
degraded |
Connected but with issues |
Door Status¶
| Status | Meaning |
|---|---|
locked |
Lock is engaged |
unlocked |
Lock is disengaged |
open |
Door is physically open |
closed |
Door is physically closed |
Function Status¶
| Status | Meaning |
|---|---|
ready |
Function ready to operate |
busy |
Function processing |
error |
Function in error state |
disabled |
Function turned off |
Fire Status¶
| Status | Meaning |
|---|---|
fire.alarm |
Fire alarm active |
fire.alarm.general |
General fire alarm active |
fire.alarm.section |
Section or zone-level alarm active |
fire.alarm.conditional |
Conditional alarm active |
fire.trouble |
Fire trouble/fault active |
fire.disabled |
Fire address, sounder, routing, or control output disabled |
fire.test |
Fire panel, group, or address in test |
fire.input.active |
Fire input address active |
fire.output.active |
Fire output/control address active |
fire.panel.day / fire.panel.night |
Fire panel operating mode |
Status Structure¶
+--------------------------------------------------+
| StatusMessage |
+--------------------------------------------------+
| AdapterId: (which adapter) |
| NodeId: (which device/thing, or Guid.Empty) |
| Data: { |
| "connection": "online", |
| "doors": { |
| "door-1": "locked", |
| "door-2": "unlocked" |
| }, |
| "lastUpdate": "2025-01-15T10:15:03Z"|
| } |
+--------------------------------------------------+
How Status Changes¶
1. Event-Driven State Change¶
Device state: locked --> unlocked
|
+---> Adapter calls generated function method
Framework updates status
Example: a device reports that a door opened. The adapter routes the event to the door Thing and calls Opened(timestamp). That call publishes the event and updates the Door function status.
2. Poll Snapshot¶
Poll interval:
+---> Adapter reads current device state
+---> Adapter writes one StatusBatch snapshot
+---> Framework publishes the changed status diff
Polling is for devices that do not push every state change. Use StatusBatch so related function states are updated atomically.
3. Framework-Managed Status¶
Connection lost:
+---> ConnectionBaseFunction updates connection status
+---> Framework propagates the effective state
Connection, lifecycle, enrollment, pending command states, and similar operational statuses are framework-owned. Adapter code should not create raw StatusMessage objects for them.
4. Startup Discovery¶
Adapter starts:
|
+---> Query all device states
+---> Build initial Thing state
+---> Framework has the first current status snapshot
Status Aggregation¶
Devices form hierarchy. Status aggregates upward:
Adapter Status: "degraded"
|
+-- Door Controller 1: "online"
| |
| +-- Reader 1: "online"
| +-- Reader 2: "offline" <-- problem here
|
+-- Door Controller 2: "online"
|
+-- Reader 3: "online"
Child "offline" -> Parent "degraded" -> Adapter "degraded"
Framework handles aggregation via FunctionStatusAggregator.
Example: Status Updates¶
// Event-driven device report: one function call updates event + status.
private static async Task HandleDoorOpened(DoorThing door, DoorEvent evt)
{
await door.Opened(evt.Timestamp);
}
// Polled device report: one batch updates the current snapshot.
public static async Task PollStatus(DoorThing door, IProtocol protocol, CancellationToken ct)
{
var state = await protocol.ReadDoorState(door.Address, ct);
await door.StatusBatch()
.Set(state.IsOpen ? DoorState.Open : DoorState.Closed)
.Commit(ct);
}
The adapter reports facts to the framework. The framework owns the status message shape, aggregation, and publishing.
Status vs Properties¶
Common confusion:
| Aspect | Status | Property |
|---|---|---|
| Question | "What state is it in?" | "How is it configured?" |
| Changes | Frequently (device reports) | Rarely (admin sets) |
| Source | Device tells adapter | PQ tells adapter |
| Examples | online/offline, open/closed | IP address, door name |
STATUS: "Door is open" (device reports this)
PROPERTY: "Unlock duration: 5s" (admin configured this)
Common Mistakes¶
1. Publishing Event Instead of Updating Status¶
// WRONG - "online" is status, not event
PublishEvent("device.online", ...);
PublishEvent("device.online", ...); // publishing same "event" repeatedly
// RIGHT - use the connection function/lifecycle path so framework updates status
await Connect(ct);
2. Publishing Status Directly From Adapter Code¶
// WRONG - adding adapter-owned status publishing plumbing
await myStatusPublisher.PublishDoorState(door, ct);
// RIGHT - update through generated functions or StatusBatch
await door.Opened(timestamp);
3. Incomplete Status on Startup¶
// WRONG - mark device online without discovering current states
await Connect(ct);
// RIGHT - query device for complete state before the first poll snapshot
var doorStates = await _protocol.GetAllDoorStates();
await ApplyInitialSnapshot(doorStates, ct);
Status Freshness¶
| Scenario | How status becomes fresh |
|---|---|
| Device pushes events | Generated function calls update status immediately |
| Device is polled | PollStatus writes a StatusBatch snapshot |
| Connection changes | ConnectionBaseFunction updates status |
| Startup | Initial discovery/poll establishes current state |
| Recovery | Reconnect flow and first successful poll refresh status |
Next: Commands - Handling action requests