# Console: the Devices screen

> Read MQTT devices and Sparkplug B nodes in the console, tune its five built-in rules and what a viewer sees.

Guide - Since version 1.7.0 - Flexway v1.7.1 - https://flexway.io/en/docs/operations-console-devices/

> Operations guide: configure, deploy and observe the gateway. What the feature does for the product: [product guide](https://flexway.io/en/docs/product-iot-devices/); calling it: [developer guide](https://flexway.io/en/docs/developer-mqtt-devices/).

The **Devices** screen (`/devices`, after Agents) shows the [MQTT device listener](https://flexway.io/en/docs/operations-mqtt-devices/) and the
[Sparkplug B nodes](https://flexway.io/en/docs/operations-sparkplug/): connections, refusals, quotas, nodes online and offline across **every**
replica. It appears as soon as the gateway declares `listeners.mqtt`; nothing to turn on in the console.

## Steps

### Reading the screen

| Part | What it shows | Who |
| --- | --- | --- |
| Summary (chosen window) | Connected devices (anonymous left out), MQTT 5 share, `PUBLISH` admitted per second, added latency p95, refusals by cause, connections by identity mode, quotas and backpressure by tenant (a link to Usage for the daily quota), cumulative sequence gaps | Everyone; a viewer, their tenant |
| Sparkplug nodes (no window, "Online", "Offline" filter) | Every replica's table merged: a node taken over by another replica is **rehomed**, never offline; the nodes of a replica that stopped answering stay listed for an hour, marked "Silent replica"; host applications and their `STATE` | Everyone; a viewer, their tenant |
| A node's panel (`/devices/<group>/<node>`) | State, devices, each replica's entry (the failover), gaps, commands admitted and refused - never a value | Everyone; failover settings and broker for admins |
| MQTT listener limits | Bounds in force, refusals by bound (8 bounds), normal closures by cause (8 causes), redirections and takeovers, the busiest replica's occupation of `max_connections`, the node table's capacity and nodes not kept | Admins, without a tenant filter |
| Tenant and device bounds | `max_connections_per_tenant`, `max_connection_rate_per_tenant`, `listeners.mqtt.tenant`, `listeners.mqtt.device` | Admins; a viewer, unless the operator hides them |
| Banners | Reconnection storm (rate refusals >= 10/min), quotas reached, broker unreachable, rehomed nodes | Depending on the data |

Node counts and rehomings come from the replicas' tables: they are still read when the metrics do not answer.

### Tuning the five built-in rules

In the console chart's values, `alerts.builtins` ([Alerting from the console](https://flexway.io/en/docs/operations-console-alerts/)):

| Rule | Default | Tunable |
| --- | --- | --- |
| `device-connections-refused` | More than 5% of `CONNECT`s refused over 5 min, from 20, `for` 5 min; the denominator counts refusals by bound; subject: `mqtt` | `threshold`, `minRequests`, `for`, `excludeCauses` (the 8 causes `authentication`, `not_authorized`, `binding`, `wildcard`, `payload_invalid`, `quota`, `packet_too_large`, `upstream_unavailable` and the 8 bounds `max_connections`, `max_unauthenticated_connections`, `max_connections_per_ip`, `max_connection_rate_per_ip`, `max_connections_per_tenant`, `max_connection_rate_per_tenant`, `connect_rate_per_identity`, `max_sessions_per_identity`), `enabled` |
| `sparkplug-node-offline` | At least 1 node offline in the merged table, `for` 2 min; a rehomed node is not offline; value: the number of nodes and tenants, never a name | `threshold` (a count), `for`, `enabled` |
| `mqtt-listener-saturation` | The busiest replica past 90% of `max_connections`, `for` 5 min; subjects: the replicas | `threshold`, `for`, `enabled` |
| `device-throttled` | **Disabled**; enabled: more than 1% of the devices held by backpressure, `for` 10 min | `enabled`, `threshold`, `for` |
| `sparkplug-sequence-gaps` | **Disabled**; enabled: at least 10 `seq` or `bdSeq` gaps over 5 min; value: the number of tenants | `enabled`, `threshold` (a count), `for` |

```yaml title="values.yaml"
# console chart values
alerts:
  builtins:
    - { name: device-connections-refused, excludeCauses: [authentication, upstream_unavailable] }
    - { name: sparkplug-node-offline, threshold: 3, for: 5m }    # a number of nodes, not a ratio
    - { name: sparkplug-sequence-gaps, enabled: true, threshold: 50 }
```

### Hiding the bounds from viewers

```yaml title="values.yaml"
# console chart values
devices:
  readerBounds: hide   # show by default; FLEXWAY_CONSOLE_DEVICES_READER_BOUNDS
```

The listener's bounds and the refusals before any identity stay the admins' whatever this setting.

## Verify

- The Devices screen lists every replica's nodes; stop a replica that drains: its nodes become "rehomed" on another,
  never offline.
- Diagnostics -> Alerts: the five rules, `device-throttled` and `sparkplug-sequence-gaps` "Disabled" unless tuned; the
  tuned threshold instead of the default.
- `GET /api/alerts`: `tuning.threshold_kind` is `count` for the two Sparkplug rules, `ratio` for the others.

## Good to know

| Topic | What to know |
| --- | --- |
| Merge | By tenant and node: the entry with the latest birth wins, `bdSeq` breaking a tie; a node seen only through its commands is not listed |
| Silent replica | The console keeps every replica's last table for an hour: its nodes stay listed, marked, unless online elsewhere |
| Lost replica | No failover trace: the node reappears online on another replica; `sparkplug-node-offline` waits 2 min |
| Reading | The console asks every replica's `GET /sparkplug/nodes` every 15 s (32 MiB at most per replica) |
| Names | Node and device names are never in a metric: no curve per node, cumulative counters |
| Not shown | MQTT bytes per second (no metric), traces per node |
| Banner thresholds | Storm: rate refusals >= 10/min; quotas: one quota or `max_connections_per_tenant` refusal; broker: one `upstream_unavailable`; rehoming: one rehomed node |
| Admin filtering one tenant | Sees the listener and the tenant's bound refusals, not the listener's limits |

*Available since `1.7.0`.*
