Pre-publish functional verification covering every feature of the gate controller. Work through the sections in order — the sequence minimises unnecessary gate trips and builds on state established in prior sections.
| Requirement | Notes |
|---|---|
| Firmware flashed to ESP32 | |
secrets.yaml populated |
WiFi, OTA password, API key |
| Device connected to Home Assistant | API active |
input_number.entry_gate_duration created in HA |
min: 1, max: 300, step: 1, unit: s |
| Gate wired and physically operational | H-bridge, endstops, motor connected |
| ESPHome web server accessible | http://<device-ip> for log inspection |
Reset state: Hold the panel Reboot/Reset button for 10+ seconds to clear any previously learned travel times and reboot clean. Manually position the gate at the closed endstop before starting.
The controller checks both endstops on boot and sets the FSM to the correct starting state automatically.
Boot the device with the gate physically at the closed endstop.
| Check | Expected | Pass |
|---|---|---|
| Close endstop binary sensor | ON | [ ] |
| FSM state (within 2s of boot) | CLOSED | [ ] |
| Open LED / Close LED | Off / Solid | [ ] |
| Cover position | 0% | [ ] |
Manually position the gate at the open endstop, then reboot.
| Check | Expected | Pass |
|---|---|---|
| Open endstop binary sensor | ON | [ ] |
| FSM state (within 2s of boot) | OPEN | [ ] |
| Open LED / Close LED | Solid / Off | [ ] |
| Cover position | 100% | [ ] |
Manually position the gate mid-travel so neither endstop is active, then reboot.
| Check | Expected | Pass |
|---|---|---|
| Both endstop sensors | OFF | [ ] |
| FSM state | STOPPED_UNKNOWN | [ ] |
| Open LED / Close LED | Alternating fast flash (opposite phase) | [ ] |
The controller measures full travel time on the first run and stores it in flash. This calibration is required before position control (Section 7) and soft stop (Section 8) are meaningful. Both directions are learned in this combined run.
During a learning run, soft start is intentionally disabled so speed is constant and the timing is accurate.
Precondition: Learned times cleared (see Before You Start). State = CLOSED.
Send OPEN from the HA cover entity.
| Check | Expected | Pass |
|---|---|---|
| FSM state | OPENING | [ ] |
| Log message | "Timing not learned: using default open time" | [ ] |
| Log message | "TIMING RUN ARMED" | [ ] |
| Soft Start Active sensor | Stays OFF for entire run | [ ] |
| Motor current | Steady from start (no soft ramp) | [ ] |
| Open LED / Close LED | Fast flash / Off | [ ] |
Gate travels to open endstop and stops.
| Check | Expected | Pass |
|---|---|---|
| Log message | "ENDSTOP: OPEN — emergency motor kill" | [ ] |
| FSM state | OPEN | [ ] |
| Learned Open Time sensor | Non-zero value (measured seconds) | [ ] |
| Gate Position sensor | ~100% | [ ] |
| Open LED / Close LED | Solid / Off | [ ] |
Precondition: State = OPEN (from 2.1).
Send CLOSE from HA.
| Check | Expected | Pass |
|---|---|---|
| FSM state | CLOSING | [ ] |
| Log message | "TIMING RUN ARMED" | [ ] |
| Soft Start Active sensor | Stays OFF for entire run | [ ] |
Gate travels to close endstop and stops.
| Check | Expected | Pass |
|---|---|---|
| FSM state | CLOSED | [ ] |
| Learned Close Time sensor | Non-zero value | [ ] |
| Open LED / Close LED | Off / Solid | [ ] |
Note the learned open and close times, then reboot the device.
| Check | Expected | Pass |
|---|---|---|
| Learned Open Time after reboot | Same as before | [ ] |
| Learned Close Time after reboot | Same as before | [ ] |
| "Timing not learned" warning on next motion | Does NOT appear | [ ] |
Precondition: Timing learned. Gate at closed endstop. State = CLOSED.
Send OPEN from the HA cover entity.
| Check | Expected | Pass |
|---|---|---|
| FSM state | OPENING | [ ] |
| Soft Start Active sensor | ON at start, transitions OFF mid-travel | [ ] |
| Log message | "Ramp complete at X% travel" | [ ] |
| FSM state at open endstop | OPEN (no countdown) | [ ] |
| Gate Position sensor | ~100% | [ ] |
Precondition: State = OPEN.
| Check | Expected | Pass |
|---|---|---|
| FSM state after CLOSE command | CLOSING | [ ] |
| Open LED / Close LED | Off / Fast flash | [ ] |
| FSM state at close endstop | CLOSED | [ ] |
| Gate Position sensor | ~0% | [ ] |
Send OPEN, wait until gate is 30–50% open, then send CLOSE.
| Check | Expected | Pass |
|---|---|---|
| FSM state after CLOSE command | CLOSING immediately (no pause) | [ ] |
| Gate reverses without stopping | Yes | [ ] |
| Gate reaches close endstop | State = CLOSED | [ ] |
Send CLOSE, wait until 30–50% closed, then send OPEN.
| Check | Expected | Pass |
|---|---|---|
| FSM state after OPEN command | OPENING immediately | [ ] |
| Gate reaches open endstop | State = OPEN | [ ] |
Precondition: State = CLOSED. Set input_number.entry_gate_duration = 10 seconds.
Press Cycle (panel button or HA button entity).
| Check | Expected | Pass |
|---|---|---|
| FSM state | OPENING_CYCLE | [ ] |
| Open LED / Close LED | Fast flash / Off | [ ] |
Gate reaches open endstop.
| Check | Expected | Pass |
|---|---|---|
| FSM state | OPEN_CYCLE | [ ] |
| Open LED / Close LED | Slow pulse (1 Hz) / Off | [ ] |
| Close Countdown sensor | Counting down from 10 | [ ] |
| Auto Close Armed sensor | ON | [ ] |
Countdown expires.
| Check | Expected | Pass |
|---|---|---|
| FSM state | CLOSING | [ ] |
| Gate closes to endstop | State = CLOSED | [ ] |
| Close Countdown sensor | Resets to 10 | [ ] |
| Auto Close Armed sensor | OFF | [ ] |
Precondition: State = OPEN_CYCLE, countdown active.
| Check after pressing Cycle | Expected | Pass |
|---|---|---|
| FSM state | OPEN (countdown cancelled) | [ ] |
| Open LED | Solid | [ ] |
| Auto Close Armed sensor | OFF | [ ] |
Precondition: State = OPEN_CYCLE, countdown active.
| Check after sending OPEN | Expected | Pass |
|---|---|---|
| FSM state | OPEN | [ ] |
| Countdown | Stopped | [ ] |
Precondition: State = OPEN_CYCLE, countdown active.
| Check after sending CLOSE | Expected | Pass |
|---|---|---|
| FSM state | CLOSING immediately | [ ] |
| Auto Close Armed sensor | OFF | [ ] |
Precondition: State = OPEN (arrived via OPEN command, no countdown armed).
| Check | Expected | Pass |
|---|---|---|
| FSM state after Cycle | CLOSING | [ ] |
| Gate closes to endstop | State = CLOSED | [ ] |
Precondition: State = OPEN_CYCLE, countdown active (set to 30s for this test).
Note the current countdown value, then briefly jog the gate away from the open endstop.
| Check | Expected | Pass |
|---|---|---|
| Countdown while off endstop | Stops decrementing | [ ] |
| Countdown after returning to endstop | Resumes from where it paused | [ ] |
Precondition: State = CLOSED.
Press Cycle, wait until mid-travel, press Cycle again.
| Check | Expected | Pass |
|---|---|---|
| FSM state after second Cycle | PAUSED_FROM_OPENING | [ ] |
| Gate | Stopped | [ ] |
| Both LEDs | Fast flash | [ ] |
Press Cycle a third time.
| Check | Expected | Pass |
|---|---|---|
| FSM state | CLOSING | [ ] |
| Gate closes to endstop | State = CLOSED | [ ] |
Precondition: State = OPEN.
Send CLOSE, wait until mid-travel, press Cycle.
| Check | Expected | Pass |
|---|---|---|
| FSM state after Cycle | PAUSED_FROM_CLOSING | [ ] |
| Both LEDs | Fast flash | [ ] |
Press Cycle again.
| Check | Expected | Pass |
|---|---|---|
| FSM state | OPENING_CYCLE | [ ] |
| Gate reaches open endstop | State = OPEN_CYCLE | [ ] |
| Countdown starts | Yes | [ ] |
Jog is a dead man's switch — the gate moves only while the button is held. It bypasses the FSM entirely; the FSM state does not change during a jog.
Precondition: State = CLOSED.
| Check while holding OPEN jog button | Expected | Pass |
|---|---|---|
| Gate movement | Moving open | [ ] |
| Open LED / Close LED | Fast flash / Off | [ ] |
| FSM state | Still CLOSED | [ ] |
| Motor Current sensor | Positive value | [ ] |
| Check after releasing OPEN jog button | Expected | Pass |
|---|---|---|
| Gate | Stops immediately | [ ] |
| FSM state | Still CLOSED | [ ] |
Precondition: State = OPEN.
| Check while holding CLOSE jog button | Expected | Pass |
|---|---|---|
| Gate movement | Moving close | [ ] |
| Open LED / Close LED | Off / Fast flash | [ ] |
| FSM state | Still OPEN | [ ] |
| Check after releasing | Expected | Pass |
|---|---|---|
| Gate | Stops immediately | [ ] |
| FSM state | Still OPEN | [ ] |
Position the gate within a few seconds of the open endstop, then hold the OPEN jog button.
| Check | Expected | Pass |
|---|---|---|
| Gate at/approaching open endstop | Motor stops (endstop lockout) | [ ] |
| FSM state | Unchanged | [ ] |
Precondition: State = OPEN_CYCLE, countdown active.
Briefly hold CLOSE jog button (gate moves off endstop), then release.
| Check | Expected | Pass |
|---|---|---|
| Countdown while off endstop | Paused (not decrementing) | [ ] |
| FSM state after releasing jog | Still OPEN_CYCLE | [ ] |
| Countdown after gate returns to endstop | Resumes | [ ] |
Send OPEN, wait until mid-travel, then press the Stop button.
| Check | Expected | Pass |
|---|---|---|
| Gate | Stops immediately | [ ] |
| FSM state | STOPPED | [ ] |
| Both LEDs | Fast flash | [ ] |
| Gate resumes on OPEN command | Yes, State = OPENING | [ ] |
Send CLOSE, wait until mid-travel, then press Stop.
| Check | Expected | Pass |
|---|---|---|
| FSM state | STOPPED | [ ] |
| Gate resumes on CLOSE command | Yes, State = CLOSING | [ ] |
During any motion, click Stop on the HA cover entity.
| Check | Expected | Pass |
|---|---|---|
| Gate | Stops immediately | [ ] |
| FSM state | STOPPED | [ ] |
Precondition: State = CLOSED.
| Check after pressing Stop | Expected | Pass |
|---|---|---|
| Gate | No movement | [ ] |
| FSM state | Still CLOSED | [ ] |
Precondition: State = PAUSED_FROM_OPENING (paused while opening).
| Check after OPEN command | Expected | Pass |
|---|---|---|
| FSM state | OPENING | [ ] |
| Gate | Continues toward open endstop | [ ] |
| Check after CLOSE command (from PAUSED_FROM_OPENING) | Expected | Pass |
|---|---|---|
| FSM state | CLOSING | [ ] |
Precondition: State = PAUSED_FROM_CLOSING (paused while closing).
| Check after CLOSE command | Expected | Pass |
|---|---|---|
| FSM state | CLOSING | [ ] |
| Gate | Continues toward close endstop | [ ] |
| Check after OPEN command (from PAUSED_FROM_CLOSING) | Expected | Pass |
|---|---|---|
| FSM state | OPENING | [ ] |
Precondition: Timing is learned (Section 2 complete).
Precondition: State = CLOSED. Set the HA cover slider to 50%.
| Check | Expected | Pass |
|---|---|---|
| FSM state during travel | OPENING | [ ] |
| Gate Position sensor | Increases during travel | [ ] |
| Soft Stop Active sensor | ON as gate approaches target | [ ] |
| Gate stops at | ~50% (±5% acceptable) | [ ] |
| FSM state after stop | STOPPED | [ ] |
| Check | Expected | Pass |
|---|---|---|
| Slider to 75% from STOPPED at 50% | Gate opens to ~75% and stops | [ ] |
| Slider to 25% from STOPPED at 75% | Gate closes to ~25% and stops | [ ] |
Precondition: State = OPEN_CYCLE, countdown active.
| Check after setting slider to 50% | Expected | Pass |
|---|---|---|
| Auto Close Armed sensor | OFF | [ ] |
| FSM state | CLOSING | [ ] |
| Gate stops at | ~50% | [ ] |
Precondition: Timing learned. State = CLOSED. Send OPEN.
| Check | Expected | Pass |
|---|---|---|
| Soft Start Active sensor at motion start | ON | [ ] |
| Motor Current sensor at start | Gradual increase (not instant) | [ ] |
| Log message | "Ramp complete at X% travel" | [ ] |
| Soft Start Active sensor after ramp | OFF | [ ] |
Observe the gate in the final approach to the open endstop.
| Check | Expected | Pass |
|---|---|---|
| Soft Stop Active sensor (last ~1% of travel) | ON | [ ] |
| Motor Current sensor near endstop | Reduces before endstop triggers | [ ] |
| Gate arrival at endstop | Clean stop, no audible slam | [ ] |
Observe the gate approaching a 50% position target.
| Check | Expected | Pass |
|---|---|---|
| Soft Stop Active sensor (last ~5% before target) | ON | [ ] |
| Gate | Visibly slows before stopping | [ ] |
| Gate stops at | ~50% | [ ] |
Precondition: Learned times reset. State = CLOSED. Send OPEN.
| Check | Expected | Pass |
|---|---|---|
| Log message | "TIMING RUN ARMED" | [ ] |
| Soft Start Active sensor | Stays OFF entire run | [ ] |
| Motor current at start | Full power from the first moment | [ ] |
Safety: Use a test object that will not be damaged and that the gate can safely contact. Test at 30–70% travel (well clear of the 0–10% and 90–100% near-endstop zones). Keep a hand near the Stop button throughout.
Precondition: Timing learned. Place obstacle in the gate path at approximately 50%.
Send OPEN and allow gate to contact the obstacle.
| Check | Expected | Pass |
|---|---|---|
| Gate stops after contact | Within 50ms (one check cycle) | [ ] |
| Log message | "OBSTACLE HIT: X.XXA > threshold at pos XX% → reversing" | [ ] |
| Gate reverses direction | Yes | [ ] |
| Reversal distance | Approximately equal to Obstacle Reverse Distance setting | [ ] |
| Log message after reversal | "Reverse complete at XX% → STOP" | [ ] |
| FSM state after reversal | STOPPED | [ ] |
Position an obstacle that also blocks the reverse path (or quickly place a second object after the first hit).
Allow the gate to hit the obstacle during its reversal.
| Check | Expected | Pass |
|---|---|---|
| Log message | "OBSTACLE DURING REVERSE: X.XXA > threshold → FAULT" | [ ] |
| Motor | Stops immediately | [ ] |
| FSM state | FAULT | [ ] |
| Open LED / Close LED | Both solid | [ ] |
Precondition: State = FAULT. Remove all obstacles.
| Check | Expected | Pass |
|---|---|---|
| After pressing Cycle | State = STOPPED | [ ] |
| Both LEDs | Fast flash | [ ] |
| After pressing Cycle again | State = OPENING_CYCLE, gate moves normally | [ ] |
Trigger an obstacle hit while the gate is in the last 10% of travel (position > 90% or < 10%).
| Check | Expected | Pass |
|---|---|---|
| Log shows threshold when hit | Base threshold × 1.15 | [ ] |
| Gate did not false-trigger on normal mechanical resistance near endstop | Yes | [ ] |
If the gate does false-trigger on normal resistance near the endstop, raise the base threshold via the Obstacle Threshold number entity in HA.
Precondition: Reduce the Obstacle Threshold entity to a low value (e.g., 0.05 A) to expose this scenario. Restore it after the test.
Send OPEN and observe the motor current spike at startup.
| Check | Expected | Pass |
|---|---|---|
| Obstacle triggered during first 300ms | No (grace period active) | [ ] |
| Log message at ~300ms | "Grace period ended, monitoring active" | [ ] |
During soft start the current readings are unreliable at low PWM, so obstacle detection is suppressed.
| Check during soft start ramp | Expected | Pass |
|---|---|---|
| Soft Start Active sensor | ON | [ ] |
| Obstacle triggered despite low threshold | No | [ ] |
| Obstacle detection activates | After Soft Start Active goes OFF | [ ] |
To simulate a timeout, physically block or disconnect the open endstop sensor so it cannot trigger. The timeout fires at 1.5 × the learned travel time.
Precondition: Timing learned. Open endstop disabled.
Send OPEN.
| Check | Expected | Pass |
|---|---|---|
| Log message | "Movement timeout armed: Xs (travel=Ys × 1.5)" | [ ] |
| After 1.5× travel time | "MOVEMENT TIMEOUT: Xs elapsed > Xs limit → FAULT" | [ ] |
| Motor | Stops immediately | [ ] |
| FSM state | FAULT | [ ] |
| Open LED / Close LED | Both solid | [ ] |
Re-enable the endstop and clear FAULT (Cycle → STOPPED, Cycle → OPENING_CYCLE).
Precondition: Learned times reset. Send OPEN.
| Check | Expected | Pass |
|---|---|---|
| Log message | "Timing not learned: using default open time" | [ ] |
| Timeout value in log | open_time_fallback × 1.5 | [ ] |
| Gate completes travel normally | Endstop stops it before timeout | [ ] |
Precondition: State = FAULT.
| Check after first OPEN command | Expected | Pass |
|---|---|---|
| FSM state | STOPPED_UNKNOWN (not yet moving) | [ ] |
| Check after second OPEN command | Expected | Pass |
|---|---|---|
| FSM state | OPENING | [ ] |
| Gate moves normally | Yes | [ ] |
| Check after holding for 1–2s and releasing | Expected | Pass |
|---|---|---|
| Log message | "too short, ignoring (need 3s+)" | [ ] |
| Device | Does not reboot | [ ] |
Note the current learned times before testing.
| Check after holding ~4s and releasing | Expected | Pass |
|---|---|---|
| Log message | "REBOOT: Button held Xs — rebooting" | [ ] |
| Device reboots | Yes | [ ] |
| Learned times after reboot | Unchanged | [ ] |
| Check after holding 10+ seconds and releasing | Expected | Pass |
|---|---|---|
| Log message | "RESET: Button held Xs — resetting learned times + rebooting" | [ ] |
| Learned Open Time after reboot | 0 | [ ] |
| Learned Close Time after reboot | 0 | [ ] |
| Warning on next motion | "Timing not learned" in logs | [ ] |
| Check after pressing Reset Learned Times button in HA | Expected | Pass |
|---|---|---|
| Learned Open Time | 0 | [ ] |
| Learned Close Time | 0 | [ ] |
Verify every entity is present, labelled correctly, and responsive. The entity prefix matches your friendly_name substitution (default: gate_controller).
| Entity | Type | Present | Responds |
|---|---|---|---|
| cover.gate | Cover (open / close / stop / position slider) | [ ] | [ ] |
| sensor — State | FSM state string | [ ] | [ ] |
| sensor — Gate Position | 0–100% | [ ] | [ ] |
| sensor — Learned Open Time | Seconds | [ ] | [ ] |
| sensor — Learned Close Time | Seconds | [ ] | [ ] |
| sensor — Close Countdown | Seconds remaining | [ ] | [ ] |
| sensor — Gate Motor Current | Amperes | [ ] | [ ] |
| binary_sensor — Open Endstop | Reflects physical switch | [ ] | [ ] |
| binary_sensor — Close Endstop | Reflects physical switch | [ ] | [ ] |
| binary_sensor — Auto Close Armed | ON during OPEN_CYCLE countdown | [ ] | [ ] |
| button — Reset Learned Times | Clears both learned times | [ ] | [ ] |
| button — Gate Cycle | Software Cycle trigger | [ ] | [ ] |
| number — Obstacle Threshold | Adjustable threshold (Amperes) | [ ] | [ ] |
| number — Obstacle Reverse Distance | Reverse distance (0.0–1.0) | [ ] | [ ] |
| sensor — WiFi Signal Strength | dBm (diagnostic) | [ ] | [ ] |
| sensor — Uptime | Human-readable (diagnostic) | [ ] | [ ] |
| binary_sensor — Status | Online / offline (diagnostic) | [ ] | [ ] |
| switch — Restart | Remote restart (diagnostic) | [ ] | [ ] |
Use the scenarios from prior sections to enter each state and verify the physical panel LEDs.
| State | Open LED (GPB0) | Close LED (GPB1) | Pass |
|---|---|---|---|
| OPEN | Solid | Off | [ ] |
| CLOSED | Off | Solid | [ ] |
| OPENING | Fast flash (2 Hz) | Off | [ ] |
| OPENING_CYCLE | Fast flash (2 Hz) | Off | [ ] |
| CLOSING | Off | Fast flash (2 Hz) | [ ] |
| OPEN_CYCLE | Slow pulse (1 Hz) | Off | [ ] |
| STOPPED | Fast flash (2 Hz) | Fast flash (2 Hz) | [ ] |
| PAUSED_FROM_OPENING | Fast flash (2 Hz) | Fast flash (2 Hz) | [ ] |
| PAUSED_FROM_CLOSING | Fast flash (2 Hz) | Fast flash (2 Hz) | [ ] |
| FAULT | Solid | Solid | [ ] |
| STOPPED_UNKNOWN | Fast flash | Fast flash (opposite phase) | [ ] |
| Jog opening (any FSM state) | Fast flash (2 Hz) | Off | [ ] |
| Jog closing (any FSM state) | Off | Fast flash (2 Hz) | [ ] |
Allow the gate to open fully and trigger the open endstop normally.
| Check | Expected | Pass |
|---|---|---|
| Log order | "emergency motor kill" appears before FSM transition message | [ ] |
| Motor Current sensor at endstop trigger | Drops to 0 immediately | [ ] |
| Visible gate overrun | None | [ ] |
Manually trigger the open endstop signal (hold the switch closed) while the gate is moving open.
| Check | Expected | Pass |
|---|---|---|
| Motor stops | Within 100ms of endstop activation | [ ] |
| Stop occurs independently | Even if scripts are mid-execution | [ ] |
Stop the gate mid-motion and watch the Motor Current sensor closely.
| Check | Expected | Pass |
|---|---|---|
| Motor Current after stop | Drops to 0 and stays at 0 | [ ] |
| Any brief current spike after stop | None (motor_kill interlock effective) | [ ] |
Allow a stop event (endstop or emergency stop) to fire while a ramp script is mid-loop.
| Check | Expected | Pass |
|---|---|---|
| Ramp scripts self-terminate | Yes (level-triggered check at top of each loop) | [ ] |
| No continued PWM after stop | Confirmed by current sensor | [ ] |
The status LED is the onboard blue LED on the Wemos D1 Mini32 (GPIO2).
| State | Status LED | Pass |
|---|---|---|
| WiFi disconnected | Fast blink (500ms on / 500ms off) | [ ] |
| WiFi connected, HA API disconnected | Solid ON | [ ] |
| WiFi connected, HA API connected | Slow breathing glow (~2s cycle) | [ ] |
Boot the device with no configured WiFi available.
| Check | Expected | Pass |
|---|---|---|
| SSID visible | gate-controller |
[ ] |
| Captive portal at 192.168.4.1 | Yes | [ ] |
Trigger an OTA update from the ESPHome dashboard.
| Check | Expected | Pass |
|---|---|---|
| Update completes | Yes | [ ] |
| Device reconnects after reboot | Yes | [ ] |
| FSM initial state correct after OTA | Matches gate physical position | [ ] |
Precondition: State = STOPPED_UNKNOWN (boot with gate mid-travel).
| Check | Expected | Pass |
|---|---|---|
| FSM state after Cycle | CLOSING | [ ] |
| Gate travels to close endstop | State = CLOSED | [ ] |
| Position now known | Normal operation resumes | [ ] |
Press Cycle 3 times in rapid succession (~200ms apart) from State = CLOSED.
| Check | Expected | Pass |
|---|---|---|
| Each press advances through valid FSM transitions | Yes | [ ] |
| No double-motion, no stuck state, no crash | Yes | [ ] |
| Check from State = CLOSED | Expected | Pass |
|---|---|---|
| Press Gate Cycle button in HA | State = OPENING_CYCLE | [ ] |
Run gate mid-travel repeatedly to allow position drift, then close and open fully.
| Check | Expected | Pass |
|---|---|---|
| Gate Position sensor at close endstop | Resets to exactly 0% | [ ] |
| Gate Position sensor at open endstop | Resets to exactly 100% | [ ] |
| Section | Description | Pass |
|---|---|---|
| 1 | Initial state detection on boot | [ ] |
| 2 | Timing learning | [ ] |
| 3 | Basic open / close / direction reversal | [ ] |
| 4 | Cycle button & auto-close | [ ] |
| 5 | Hold-to-operate (jog) | [ ] |
| 6 | Stop & pause mid-travel | [ ] |
| 7 | Position control | [ ] |
| 8 | Soft start & soft stop | [ ] |
| 9 | Obstacle detection | [ ] |
| 10 | Movement timeout & FAULT | [ ] |
| 11 | Reboot & reset button | [ ] |
| 12 | Home Assistant entity audit | [ ] |
| 13 | LED pattern verification | [ ] |
| 14 | Safety architecture | [ ] |
| 15 | Connectivity & status LED | [ ] |
| 16 | Edge cases | [ ] |
Tester: _____________________________ Date: _______________ Firmware: _______________