Implementing Critical IoT Sensor Alerts in a Mobile Application
Temperature in the server room climbed to 45°C at 3 AM. The push notification arrived at 9 AM — when the smartphone connected to Wi-Fi. By then, equipment overheated. IoT alerts are a task where "delayed notification" is worse than no notification at all.
Why Standard FCM Isn't Suitable for Critical IoT Alerts
Standard FCM priority (normal) buffers delivery when a device is in Doze Mode. Fine for non-critical notifications. For IoT alerts — unacceptable.
You need "priority": "high" in FCM payload plus on iOS apns-priority: 10 with interruption-level: critical. The latter is special: UNNotificationInterruptionLevel.critical plays sound even in Do Not Disturb mode and with silent mode enabled. Requires special entitlement com.apple.developer.usernotifications.critical-alerts, which must be requested separately from Apple.
Request permission for critical notifications — a separate prompt, different from standard:
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge, .criticalAlert]
) { granted, error in ... }
User must explicitly allow critical notifications — can't enable without consent.
IoT Pipeline Architecture
Sensors → MQTT broker (Mosquitto or AWS IoT Core) → server handler → FCM/APNs.
MQTT is the de-facto standard for IoT: lightweight protocol, works over unstable connections, supports QoS 0/1/2. Sensors publish data to topic sensors/{device_id}/temperature, server subscribes to all user device topics.
Server handler checks incoming values against threshold rules:
const rules = await getRulesForDevice(deviceId);
for (const rule of rules) {
if (rule.condition(value)) {
await sendCriticalAlert(userId, {
sensor: deviceId,
metric: rule.metric,
value,
threshold: rule.threshold,
severity: rule.severity
});
}
}
Deduplication is mandatory. If a sensor sends data every 10 seconds and temperature stays above threshold for 30 minutes — that's not 180 notifications, but one with status updates. Redis: SET alert:{device}:{metric}:active 1 EX 1800 — while key exists, don't send new alerts for this condition.
Multi-Level Alert System
| Level | Example | FCM priority | iOS level | Action |
|---|---|---|---|---|
| Info | Sensor battery 20% | normal | passive | In shade |
| Warning | Temperature >35°C | high | active | Wakes screen |
| Critical | Temperature >45°C | high | critical | Sound in silent |
| Emergency | CO sensor >200 ppm | high | critical | Sound + vibration |
On Android similarly via notification channels with different importance: IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, IMPORTANCE_MAX.
Mobile Application: Monitoring Screen
Dashboard with live sensor data — implemented via WebSocket connection (not polling, to see real-time updates while app is open). On Flutter: web_socket_channel package, data in Riverpod StreamProvider.
Historical charts — fl_chart or syncfusion_flutter_charts. Store history on server in InfluxDB or TimescaleDB (PostgreSQL extension) — both optimized for time series.
Threshold rule configuration in app: user selects sensor, metric, operator (>, <, ==), value, severity level. Rules saved on server.
Development Process
- Choose MQTT broker and topic schema
- Develop server handler with deduplication
- Set up FCM + APNs critical alerts entitlement
- Develop mobile client (dashboard, alert settings)
- Test delivery in Doze Mode and Do Not Disturb
- Load test (many sensors simultaneously)
Timeline: from 4 weeks (integration with existing IoT infrastructure) to 10–12 weeks (full stack including MQTT broker, server, and mobile client).







