Reliable command delivery to ESP32‑H2 Zigbee sleepy end device (ZHA)

Hi all,

I’m building a battery-powered Zigbee door lock using an ESP32‑H2 (Arduino Zigbee) and running it through Home Assistant (ZHA + SkyConnect).

The core issue I’m facing is that commands sent from Home Assistant are only executed if the device happens to be awake at the exact moment of sending, and I have not found a way to make command delivery reliable for a sleeping device.

I’ve tried several approaches (polling, wake signals, delayed commands, etc.), but all of them run into what seems like a fundamental limitation in how ZHA and sleepy Zigbee devices interact.

I’m now trying to determine whether: this is an inherent limitation or if I’m missing a known design pattern
Setup

ESP32‑H2 (battery-powered)

Zigbee end device (not a router)

Arduino Zigbee (not ESP‑IDF)

Exposed as a simple On/Off device (ZigbeeLight)

Controls a DC motor to lock/unlock

Uses deep sleep for battery life

Wake sources:

External button (manual wake)

Periodic timer (polling)

Coordinator:

Home Assistant ZHA

SkyConnect (ZBT‑1)

Intended behavior

The goal is a typical smart lock workflow:

Send lock/unlock command from Home Assistant at any time

Device wakes periodically or on demand

Command is delivered reliably

Motor executes

Device goes back to sleep

Actual behavior

If the device is asleep → HA attempts command for ~30 s → times out

If I wake the device after sending the command → nothing happens

Repeating commands → sometimes works, often does not

HA does not queue or retry commands after failure

Availability state does not properly reflect wake cycles

What I have tried

  1. Periodic polling

    Wake every ~10 s

    Short active window

    → Commands sometimes succeed, but not reliably

    Behavior is clearly timing-dependent

  2. Availability-based logic

    Tried triggering automations on device availability

    Found that:

     Availability does not change for short wake cycles
    
     Device often remains unavailable
    

    → Not usable as a wake indicator

  3. “Send then wake”

    Send command from HA

    Then manually wake the device

    → Does not work

    The command has already been discarded by HA

  4. Binary sensor “wake beacon”

    Device reports “awake” via Zigbee

    HA waits for signal before sending command

    → Not reliable

    HA automation processing is not synchronized with Zigbee delivery

  5. Enforced awake window (device stays awake several seconds)

    Keep device awake ~3–5 seconds after wake

    → Leads to instability in Arduino Zigbee

     erratic serial output
    
     broken behavior
    

    Suggests the Arduino Zigbee stack is not designed for this pattern

Observations / conclusions so far

Based on testing:

HA/ZHA requires confirmed delivery (ACK) before updating state

Commands are not queued for later execution

Availability is not a reliable wake signal

Command delivery depends on tight timing overlap

Arduino Zigbee provides limited control over message timing

This results in unavoidable race conditions when the device is asleep.
My question

Is reliable command delivery to a battery-powered Zigbee end device possible in ZHA without implementing Poll Control (cluster 0x0020)?

More specifically:

Is this fundamentally how ZHA handles sleepy devices?

Is Arduino Zigbee too limited for this type of device?

Has anyone successfully built a battery-powered Zigbee actuator (e.g., lock) that:

    is not always on

    and does not implement Poll Control?

Possible next steps I’m considering

Switching to ESP‑IDF Zigbee to implement Poll Control properly

Trying Zigbee2MQTT to compare behavior

Keeping the device awake longer (but concerns about stability remain)

Moving away from sleepy device design

Looking for

Confirmation if this is a hard limitation

Known design patterns for Zigbee locks

Experiences from others working with:

    ESP32‑H2 Zigbee

    sleepy actuators (not just sensors)

    ZHA limitations and behavior