Skip to content

Rapid successive button presses cause spurious state inversion (~500ms) due to blocking asyncio.sleep in HTTP handler #21

@Foroxon

Description

@Foroxon

Description

When a physical switch is pressed twice in quick succession, or two different switches are pressed one after the other within ~300ms, the last triggered relay state is spuriously inverted roughly 500ms later.

Example: Light is OFF. Two quick presses → expected: OFF→ON→OFF. Actual: OFF→ON→OFF→ON (final state inverted after ~500ms).


Root Cause

_turn_off_state in MegaDCoordinator uses await asyncio.sleep(0.5) to auto-reset Click-mode sensor state. This await runs inside the HTTP request handler (MegadHttpView.getupdate_port_state_turn_off_state).

MegaD firmware sends HTTP notifications sequentially — it waits for an HTTP 200 response before sending the next one. When HA blocks the HTTP response for 500ms (the asyncio.sleep), all subsequent MegaD notifications are queued. They then arrive out of order: most critically, relay state notifications (e.g. pt=9&v=0) arrive after the delay and incorrectly update relay state in HA, causing the spurious inversion.


Environment

Component Version
Home Assistant 2026.5.4
Integration (megad) v2026.5.1
MegaD firmware 4.59b9
MegaD model MegaD-2561

Port Configuration Used for Reproduction

Port 2 — IN, Click mode (C):

Act: 9:2||11:255

Single click → toggle relay 9; || skips double-click; long press → PWM 11 = 255

Port 4 — IN, Click mode (C):

Act: 11:*25||11:1

Single click → dim PWM 11 by 25%; || skips double-click; long press → PWM 11 = 1

Port 9 — OUT, SW (relay), Default = 0

Port 11 — OUT, PWM, Default = 0


Steps to Reproduce

Scenario A (same switch, two quick presses):

  1. Light (relay 9) is OFF
  2. Press switch (port 2) twice within ~300ms
  3. Expected final state: OFF. Actual: the light flips back ON ~500ms after the second press

Scenario B (two different switches, pressed in quick succession):

  1. Press switch port 4 (triggers action on port 11/9)
  2. Immediately press switch port 2 (triggers toggle on relay 9)
  3. Observe: relay 9 changes state, then inverts ~500ms later

Logbook Evidence — Before Fix

The 502ms gap between sensor.md2_port4 single and sensor.md2_port4 off reveals MegaD was blocked waiting for HA's HTTP response:

10:54:42.597  sensor.md2_port4  single
10:54:43.099  sensor.md2_port4  off       ← 502ms gap (MegaD blocked by HA's sleep)
10:54:43.108  light.md2_port9   on
10:54:43.119  light.md2_port11  off
10:54:43.129  sensor.md2_port2  single
10:54:43.631  sensor.md2_port2  off
10:54:43.641  light.md2_port9   off       ← spurious inversion

Proposed Fix

In custom_components/megad/__init__.py, MegaDCoordinator.update_port_state (~line 218):

# Before — blocks HTTP response for 500ms:
if isinstance(port, ReaderPort) or port.conf.mode == ModeInMegaD.C:
    await self._turn_off_state('off', 0.5, port_id, data)

# After — returns HTTP response immediately:
if isinstance(port, ReaderPort) or port.conf.mode == ModeInMegaD.C:
    self.hass.async_create_task(
        self._turn_off_state('off', 0.5, port_id, data)
    )

async_create_task schedules _turn_off_state as a background coroutine. HA responds to MegaD in ~5–40ms instead of 500ms, preventing notification queuing and out-of-order delivery.


Logbook Evidence — After Fix

11:16:06.252  sensor.md2_port2  single
11:16:06.291  light.md2_port9   on        ← response in 39ms (was 500ms+)
11:16:06.750  sensor.md2_port2  off
11:16:07.110  sensor.md2_port2  single
11:16:07.124  light.md2_port9   off       ← clean toggle, no spurious inversion
11:16:07.821  sensor.md2_port2  off

Fix tested and confirmed working with rapid repeated presses across multiple ports.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions