Chapters

50 Interaction Patterns: Synchronization and Recovery

ux-design
interface
interaction
patterns

50.1 Start With the Decision

A thermostat app, wall unit, web page, and voice tool may show different states. Sync rules must settle which state is true.

50.2 Route Overview

This is part 2 of 2. Review Interaction Patterns: State and Feedback for the preceding evidence.

50.3 Learning Objectives

  • Analyse Ecobee polling and state sync tradeoffs.
  • Design recovery for stale, failed, and conflicting updates.

50.4 Chapter Roadmap

  • Ecobee State Synchronization
  • Checkpoint: Sync Implementation
  • Common Mistakes
  • Interaction Pattern Pitfalls
  • Knowledge Check
  • Quiz: Interaction Patterns
  • Optimistic UI for Garage Doors
  • Checkpoint: Recovery States
  • State Sync Strategy Choices
  • Pending vs Confirmed States
  • Checkpoint: Safety and Accessibility
  • Interactive Quiz: Match Concepts
  • Interactive Quiz: Sequence the Steps
  • Common Pitfalls
  • Map to User Mental Models
  • 2. Over-Relying on Icons Without Labels
  • 3. Ignoring State Transition Feedback
  • Label the Diagram
  • Code Challenge
  • Summary
  • For Kids: Meet the Sensor Squad!
  • Concept Relationships
  • See Also
  • In 60 Seconds
  • Try It Yourself
  • What’s Next

50.5 Ecobee State Synchronization

Ecobee’s smart thermostat (launched 2015) faced a state synchronization challenge that illustrates why distributed state management matters. The thermostat has four interfaces: the device’s touchscreen, the smartphone app, the web portal, and voice assistants (Alexa, Google Assistant). Early versions used a polling-based synchronization model where each interface checked the cloud for updates every 30 seconds.

Read this case as a state-lifecycle walkthrough. Establish the authoritative setpoint first, then follow one wall-unit change through the local display, cloud record, mobile and web views, and voice response. At each hand-off ask whether the value is pending, confirmed, stale, rejected, or queued offline. Next compare the polling delays with the later push path, and inspect how optimistic display, reconciliation, ordering, and conflict handling affect what each person sees. The design lesson is broader than lower latency: distributed controls need an explicit authority model, version or ordering evidence, visible intermediate states, and a recovery rule when interfaces disagree. Without those contracts, a fast interface can still present a convincing but obsolete value and invite a second command based on false state.

The problem in numbers:

InterfaceSync MethodLatencyUser Complaint
TouchscreenDirect (local)<100 msNone
Mobile appCloud poll every 30s0-30 seconds“App shows wrong temperature”
Web portalCloud poll every 60s0-60 seconds“Outdated readings”
Voice assistantOn-demand API call2-5 seconds“Alexa says 72 but screen says 68”

When a user adjusted the temperature on the wall unit, the app could show stale data for up to 30 seconds. During that window, if someone else opened the app and adjusted the temperature based on the outdated reading, a “temperature war” ensued — the system oscillated between competing setpoints.

Ecobee’s resolution (2017 firmware update):

  1. Push-based sync via WebSocket: Replaced polling with persistent WebSocket connections. State changes propagate to all connected interfaces within 1-2 seconds.
  2. Optimistic UI with reconciliation: The app shows the new setpoint immediately when the user drags the temperature slider, with a subtle pulsing animation (“pending”) until the device confirms.
  3. Conflict resolution with timestamp: Each state change carries a millisecond-precision timestamp. Last-write-wins ensures deterministic behavior during simultaneous adjustments.
  4. Offline queue with merge: When connectivity drops, commands queue locally and replay in order on reconnection. If the device state has changed during the outage, the user sees a notification: “Temperature was changed while you were offline.”

Measurable outcomes:

  • Cross-interface sync latency: 30 seconds reduced to 1.5 seconds average
  • “Wrong temperature” support tickets: decreased 74%
  • User-reported “temperature wars”: decreased 89%
  • App session engagement: increased 23% (users trusted the displayed data)

Design lesson: For any IoT product with multiple control interfaces, push-based synchronization with optimistic UI is not optional — it is the minimum viable architecture. Polling creates a window of inconsistency that erodes user trust in every interface.

UX UmaCheckpoint: Sync Implementation

You now know:

  • Commands need ids so retries do not create duplicate physical actions; pending commands need a timeout path such as 10 seconds.
  • Polling every 30 seconds or 60 seconds creates stale-interface windows; push-based WebSocket or MQTT updates can reduce cross-interface lag to about 1-2 seconds.
  • Support evidence should use the UI vocabulary, so “timeout,” “offline,” “confirmed,” and “changed while offline” mean the same thing in logs and screens.

50.6 Common Mistakes

Interaction Pattern Pitfalls

50.6.1 Mistake 1: Unclear State Indication

The Problem: Toggle switches and buttons that don’t clearly show current state, leaving users guessing.

Real Example: A smart plug app has a toggle labeled “Power.” When the toggle is to the right, does that mean the power is ON, or that tapping will turn it ON?

The Fix:

Bad DesignGood Design
Toggle labeled “Power”Status: “CURRENTLY ON” with button labeled “Turn Off”
Button labeled “Lock”Status: “Unlocked” with button labeled “Lock Door”
Slider with no labels“Brightness: 75%” with slider showing current value

Design Principle: Show state (what is true now) separately from controls (what you can do).

50.6.2 Feedback for Delayed Actions

The Problem: Commands sent to IoT devices take 1-5 seconds due to network latency, but UI provides no feedback, leading users to tap repeatedly.

Real Example: User taps “Lock Door” in app. Nothing happens visually for 3 seconds. User taps again, thinking it failed. Door locks, then unlocks.

The Fix: Implement optimistic UI updates with loading states:

User ActionImmediate Feedback (0-100 ms)During Processing (1-5s)On SuccessOn Failure
Lock doorButton shows “Locking…”Spinner + greyed-out state“Locked” (green)“Failed to lock” + retry button
Set temperatureDisplay updates to new temp“Sending to device…”Temperature shows on device“Device offline” + queue for later

Design Principle: Acknowledge immediately (< 100 ms), show progress (1-5s), confirm completion, prevent double-submission.

50.7 Knowledge Check

Quiz: Interaction Patterns
Optimistic UI for Garage Doors

Scenario: A smart garage door takes 8-12 seconds to fully open or close (physical mechanical operation). Network round-trip adds 200-500 ms latency. Users complained about unresponsive UI.

Without Optimistic UI (Original Implementation):

// BAD: User waits for server confirmation before UI updates
async function toggleDoor() {
    showSpinner();  // Show loading spinner
    const response = await fetch('/api/garage/toggle'); // 8-12 seconds total
    if (response.ok) {
        updateUI('open');  // Finally update UI
    } else {
        showError('Command failed');
    }
}

User Experience: Tap button → Spinner for 8-12 seconds → UI updates. Users tap multiple times thinking it didn’t work, sending duplicate commands.

With Optimistic UI (Improved Implementation):

// GOOD: Immediate UI feedback with reconciliation
async function toggleDoor() {
    const previousState = doorState;  // Save current state
    const targetState = (previousState === 'closed') ? 'opening' : 'closing';

    // Step 1: Immediate optimistic update (< 100ms)
    updateUI(targetState);  // Show "Opening..." immediately
    showProgressIndicator();  // Visual feedback: door animating

    try {
        // Step 2: Send command to device
        const response = await fetch('/api/garage/toggle', {
            method: 'POST',
            body: JSON.stringify({ target: targetState })
        });

        if (response.ok) {
            // Step 3: Poll for actual state confirmation
            const finalState = await pollDoorState(12000);  // 12s timeout
            updateUI(finalState);  // Confirm: "Open" or "Closed"
            clearProgressIndicator();
        } else {
            // Step 4: Rollback on failure
            updateUI(previousState);  // Revert to previous state
            showToast('Door command failed - check if door is obstructed');
        }
    } catch (error) {
        // Network error - revert optimistically updated state
        updateUI(previousState);
        showToast('Connection lost - door may not have moved');
    }
}

async function pollDoorState(timeout) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeout) {
        const state = await fetch('/api/garage/state').then(r => r.json());
        if (state === 'open' || state === 'closed') {
            return state;  // Final state reached
        }
        await sleep(1000);  // Poll every 1 second
    }
    throw new Error('Timeout waiting for door state');
}

UI State Machine:

TimeDisplayed StateActual Door StateVisual Indicator
T+0 ms“Opening…”Still closedAnimated progress bar
T+100 ms“Opening…”Starting to openProgress bar at 10%
T+4s“Opening…”Halfway openProgress bar at 50%
T+8s“Opening…”Fully openProgress bar at 90%
T+8.5s“Open” ✓Fully openGreen checkmark

Error Recovery Scenarios:

Error ConditionUI ResponseUser Communication
Network timeoutRevert to previous state + warning“Connection lost - verify door state visually”
Door obstructedShow “Opening…” then “Error”“Door stopped - check for obstruction”
Duplicate commandIgnore if state matchesNo change (already opening)
Door sensor failureShow last known state + ”?”“Cannot verify door state - sensor offline”

Measured Results:

MetricWithout Optimistic UIWith Optimistic UIImprovement
Time to visual feedback8-12 seconds< 100 ms99% faster
Duplicate command rate43% of users2% of users95% reduction
User satisfaction (SUS)52 (poor)78 (good)+50%
Support calls about “broken button”28/month1/month96% reduction
Average taps per action2.71.0262% reduction in duplicate commands

Perceived Responsiveness Calculation:

Perceived delaybefore=12,000 ms(blocking UI)\text{Perceived delay}_{\text{before}} = 12,000 \text{ ms} \quad \text{(blocking UI)} Perceived delayafter=50 ms(optimistic update)\text{Perceived delay}_{\text{after}} = 50 \text{ ms} \quad \text{(optimistic update)} Improvement=12,00050=240× faster\text{Improvement} = \frac{12,000}{50} = \mathbf{240\times \text{ faster}}

Key Lesson: For any IoT action with > 500 ms latency, optimistic UI is not optional—it’s required for acceptable UX. The actual door operation time (8-12 seconds) didn’t change, but perceived responsiveness improved 240× by acknowledging the tap in 50 ms instead of making users wait 12 seconds for confirmation. Always provide immediate visual feedback, then reconcile with actual state asynchronously.

UX UmaCheckpoint: Recovery States

You now know:

  • For slow mechanical actions, show the transition state immediately, then confirm the final state after the device reports it.
  • A failed command should revert or mark uncertainty, not leave the optimistic state looking final.
  • Duplicate commands, obstruction, sensor failure, and network timeout need different messages because each asks for a different next action.
State Sync Strategy Choices
Device CharacteristicSync StrategyUpdate MechanismConflict Resolution
Always online (Wi-Fi smart bulb)Push via WebSocketDevice publishes state changes immediatelyLast-write-wins with timestamp
Intermittently online (battery sensor)Poll + offline queueApp polls every 30s; device queues commands when offlineMerge queues on reconnection
Multiple physical controls (thermostat with wall dial + app)Authoritative device stateAll interfaces subscribe to device; device is source of truthDevice state overrides app optimistic updates
Low-latency required (smart lock)Local mesh + cloud syncBLE mesh for local control; cloud sync for remote accessLocal control always wins
High-frequency updates (sensor data)Throttle + aggregateDevice sends deltas only (> 0.5°C change); cloud aggregatesLatest value in 5-second window

Synchronization Pattern Selection:

ScenarioPatternTrade-offs
Smart home with app + voice + wall switchesPub/sub via MQTT brokerAll interfaces subscribe to devices/{id}/state topic. Device publishes on change. Low latency (< 500 ms), requires broker.
Industrial equipment with local HMI + cloud dashboardAuthoritative device with periodic cloud syncDevice maintains state; HMI reads local; cloud polls every 10s. Resilient to connectivity loss, cloud data may be stale.
Wearable with phone appBluetooth GATT with NOTIFYDevice notifies app on state change via BLE characteristic. Very low latency (< 50 ms), limited range.
Fleet management with thousands of vehiclesEvent-sourcing with eventual consistencyVehicles log state changes; cloud replays events to rebuild state. Scales to millions of devices, complexity in conflict resolution.

Conflict Resolution Strategies:

Conflict TypeResolution RuleExample
Simultaneous changesLast-write-wins with server timestampUser A sets temp to 70°F at 14:30:00, User B sets to 72°F at 14:30:01 → 72°F wins
Offline editsOperational transform or CRDTDevice accumulates +3°C while offline, cloud has -1°C → Merge to +2°C net change
Physical overridePhysical control always winsUser manually adjusts wall thermostat → App optimistic update is discarded
Safety-criticalConservative merge (safer option)Smoke detector armed remotely + locally disarmed → Armed state wins (fail-safe)
Shared-household preferenceNamed policy, not a hidden defaultTwo residents set different target temperatures → the system applies whichever a chosen policy names: last-command-wins, priority voting among linked accounts, or account-owner override

The shared-household row is a different kind of conflict from the rows above it. Simultaneous-change and offline-edit conflicts are races between two commands that both look valid; the timestamp or merge rule settles them without a person needing to know a race happened. A shared-household preference conflict is a disagreement between two people who both have permission to act, and simply picking whichever command arrived last hides that disagreement instead of resolving it. A multi-user product should make the resolution policy visible in account or household settings — last-command-wins for a household that wants simplicity, a vote or shared-schedule negotiation for a household that wants consensus, or an admin/owner override for a rental or managed-property setup — rather than silently applying one rule and letting residents discover the policy through repeated conflict.

Anti-Pattern: Using polling-only synchronization with 30-60 second intervals. Creates inconsistency windows where users see stale data and make conflicting changes.

Pending vs Confirmed States

What Practitioners Do Wrong: Implementing optimistic UI that shows the new state identically to a confirmed state, leaving users unable to distinguish “command sent” from “command completed.”

The Problem: When a user taps “Lock Door,” the UI immediately shows a locked icon. If the command fails (network error, battery dead, door jammed), the UI still shows “locked” for several seconds until the error is detected. During this window, the user believes the door is locked when it’s actually unlocked—a security risk.

Real-World Example: A smart lock app used optimistic UI without status indicators. User testing revealed:

  • 68% of users left their home within 10 seconds of tapping “Lock”
  • Average error detection time: 4.2 seconds
  • In error scenarios, users were already outside before the app showed “Failed to lock”
  • Result: 34% of users in error scenarios did not return to manually check the door

The Correct Implementation — Visual State Machine:

// Define distinct visual states
const UIStates = {
    UNLOCKED: {
        icon: 'unlocked',
        color: 'red',
        label: 'Unlocked',
        ariaLabel: 'Door is unlocked'
    },
    LOCKING: {
        icon: '🔒',
        color: 'yellow',
        label: 'Locking...',
        opacity: 0.6,  // Dim to indicate pending
        spinner: true,  // Show activity indicator
        ariaLabel: 'Sending lock command to door'
    },
    LOCKED: {
        icon: '🔒',
        color: 'green',
        label: 'Locked ✓',
        ariaLabel: 'Door is locked and confirmed'
    },
    ERROR: {
        icon: '✗',
        color: 'red',
        label: 'Lock Failed',
        ariaLabel: 'Door failed to lock - check manually'
    }
};

async function lockDoor() {
    setState(UIStates.LOCKING);  // Optimistic: show pending state

    try {
        const response = await sendLockCommand();
        if (response.confirmed) {
            setState(UIStates.LOCKED);  // Confirmed by device
        } else {
            setState(UIStates.ERROR);
            showAlert('Door did not lock - verify manually');
        }
    } catch (error) {
        setState(UIStates.UNLOCKED);  // Revert on network error
        showAlert('Command failed - door still unlocked');
    }
}

Visual Distinction Techniques:

StateVisual IndicatorDurationUser Understanding
PendingDimmed (60% opacity) + spinnerUntil confirmed or timeout“Command sent, waiting”
ConfirmedFull opacity + checkmarkPersistent“Action complete”
FailedRed X + error messageUntil dismissed“Action did not complete”

Accessibility Requirements (WCAG 2.1):

  • Don’t rely on color alone: Combine color (yellow/green/red) with text labels (“Locking…” vs “Locked”)
  • Provide aria-live updates: <div aria-live="polite" aria-atomic="true">Sending lock command...</div>
  • Use aria-busy attribute: <button aria-busy="true">Locking...</button> while pending

Measured Impact of Proper State Indication:

MetricWithout State DistinctionWith Pending IndicatorsChange
Users who noticed command failures32%91%+184%
Average time to detect error8.4 seconds1.2 seconds86% faster
Users who verified door manually after error41%89%+117%

Key Lesson: Optimistic UI requires three distinct visual states: pending (optimistic), confirmed (reconciled), and failed (rolled back). Never show a pending state identically to a confirmed state, especially for safety-critical actions.

UX UmaCheckpoint: Safety and Accessibility

You now know:

  • Pending and confirmed states must look and read differently, especially for locks, alarms, pumps, heaters, and access control.
  • Accessibility is part of the state machine: do not rely on color alone, use text labels, and announce important status changes.
  • A muted notification, dismissed notification, acknowledged incident, and resolved event are separate states, so store and display them separately.
Interactive Quiz: Match Concepts
Interactive Quiz: Sequence the Steps

Common Pitfalls

Map to User Mental Models

Creating interaction flows that make sense to engineers but contradict users’ existing mental models from smartphones and web applications produces steep learning curves and abandonment. Map every primary interaction to an existing familiar pattern before inventing new paradigms.

2. Over-Relying on Icons Without Labels

Icon-only interfaces that appear clean in design reviews fail when users cannot identify what an icon means without trying it. Pair icons with text labels in primary navigation and reserve icon-only presentation for secondary or expert-level interactions where meaning is established.

3. Ignoring State Transition Feedback

Interactions that change device state (locking a door, arming a sensor) without immediate visual or auditory feedback leave users uncertain whether their action was registered, often triggering repeated taps. Acknowledge every state change with a clear animation, LED change, or sound within 200 ms.

Label the Diagram
Code Challenge

50.8 Summary

This chapter covered essential interaction patterns for IoT interfaces:

Key Takeaways:

Optimistic UI: Provide immediate feedback (< 100 ms), show progress during network operations, reconcile on success/failure. State Synchronization: Device state is authoritative, all interfaces subscribe to updates, last-write-wins for conflicts. Notification Escalation: Five severity levels from silent logging to emergency alerts, with automatic escalation on non-response. Feedback Matching: Critical actions need immediate + haptic, background tasks need completion notifications.

For Kids: Meet the Sensor Squad!

Interaction patterns are the secret rules that make smart devices feel smooth and responsive!

50.8.1 Impatient Button Press

the microcontroller built a smart light switch for the living room. You pressed the button in the app, and… nothing happened for 3 seconds. Then the light turned on.

“Is it broken?” asked Temperature Terry, pressing the button again. Now the light turned on, then off, then on again! “I pressed it three times because I thought it wasn’t working!” groaned Sammy.

the LED had an idea. “What if the button IMMEDIATELY shows the light is on — even before the message reaches the actual light? That way, Sammy sees instant feedback!”

They called this trick Optimistic UI — the app shows “light is ON” right away and trusts that the message will get through. If something goes wrong, it changes back and says “Oops, the light didn’t respond. Try again?”

Next problem: Dad changed the thermostat using the wall dial, but Mom’s phone app still showed the old temperature! “Why does my app say 70 when Dad just set it to 72?” asked Mom.

“We need STATE SYNC!” explained the battery. “When ANYONE changes something — the wall dial, Mom’s phone, Dad’s phone, or even a voice command — ALL of them should update at the same time. Like a group text message for devices!”

Finally, the security camera was sending 50 notifications a day: “Motion detected!” for every squirrel, leaf, and passing car. Everyone turned off notifications… and missed a real delivery!

“We need NOTIFICATION ESCALATION!” said Sammy. “Squirrels get a silent note in the log. Delivery trucks get a quiet badge on the app. But a person at the door at midnight? THAT gets a LOUD alert!”

50.8.2 Key Words for Kids

WordWhat It Means
Optimistic UIShowing the result instantly (before it actually happens) so the app feels super fast
State SyncMaking sure ALL devices show the same information at the same time
Alert FatigueWhen you get SO many notifications that you ignore ALL of them, even important ones
Notification EscalationUsing quiet alerts for small things and loud alerts for important things
Concept Relationships

How this chapter connects to other IoT concepts:

Builds on: Interface Design Fundamentals provides the UI component hierarchy foundation that interaction patterns operate within. Enables: Multimodal Design uses optimistic UI and state sync patterns across voice, touch, and gesture interfaces. Supports: Design Process & Checklists validation relies on proper implementation of feedback and state synchronization. Applied in: Communication Networks understanding network latency characteristics informs optimistic UI timing parameters. Privacy consideration: Privacy and Compliance distributed state synchronization affects data residency and GDPR compliance.

See Also

Related topics for deeper exploration:

MQTT Protocol: The pub/sub messaging pattern used in the state synchronization example is implemented via MQTT in production IoT systems. WebSocket Communication: Real-time bidirectional communication for push-based state updates mentioned in the Ecobee case study. Event-Driven Architecture: Notification escalation and geofence triggers are examples of event-driven IoT design. Accessibility Standards: WCAG 2.1 requirements for aria-live regions and screen reader compatibility in optimistic UI. Network Quality of Service: Understanding network latency variability helps design appropriate timeout and retry logic.

In 60 Seconds

This chapter covers interface design: interaction patterns, explaining the core concepts, practical design decisions, and common pitfalls that IoT practitioners need to build effective, reliable connected systems.

Try It Yourself

Hands-on exercises to reinforce interaction pattern concepts:

50.8.3 Optimistic UI for Smart Locks

Build a simple web interface that demonstrates optimistic UI with rollback:

  1. Create an HTML button labeled “Lock Door”
  2. On click, immediately show “Locked” state with loading spinner
  3. Simulate 2-second network delay with setTimeout
  4. 80% of the time, confirm lock success; 20% simulate failure
  5. On failure, revert to “Unlocked” and show toast message

What to observe: Notice how instant feedback feels more responsive than waiting 2 seconds. Test clicking the button multiple times rapidly—without optimistic UI, users queue multiple commands. With it, the button disables during pending state.

50.8.4 Exercise 2: Test Notification Fatigue

Configure a smart home simulator with motion sensors:

Set sensors to trigger notifications for every motion event. Simulate 50 motion events in 10 minutes (typical busy household). Track when you start ignoring notifications. Re-configure with escalation: silent log (trees), badge (routine), push (unusual), alarm (security).

What to observe: Notice the point where you mentally tune out notifications. Compare notification counts: 50 vs. 3-5 after escalation filtering.

50.8.5 Exercise 3: State Sync Race Condition

Create a multi-client simulation:

  1. Open the Wokwi ESP32 MQTT simulator in two browser tabs
  2. Both tabs subscribe to home/thermostat/temperature topic
  3. Both tabs publish temperature changes simultaneously
  4. Observe last-write-wins behavior

What to observe: Which client’s change “wins”? Notice how timestamp resolution affects conflict detection. See what happens if clocks are skewed between clients.

50.9 What’s Next

Next TopicDescription
Multimodal InteractionVoice, touch, gesture modalities with accessibility and graceful degradation
Process & ChecklistsIterative design methodology and validation checklists
Worked ExamplesVoice interface design case study for elderly users
Hands-On LabBuild an accessible IoT interface with ESP32 and OLED

50.10 Continue Your Route

This final part closes the route from Ecobee State Synchronization through What’s Next. Return to Interaction Patterns: State and Feedback or continue from the ux-design module index.