A Wide Deadband for a Short-Cycling Basement Dehumidifier

Contents
The problem
A dehumidifier in a basement has a humidistat knob, and behind that knob is a control with almost no hysteresis. It reaches setpoint, shuts the compressor off, and about thirty seconds later — as soon as the air near the sensor drifts back up a fraction of a percent — it starts it again. All day. The compressor short-cycles, which is hard on it (every start is an inrush surge and a high-head restart if the refrigerant pressures haven’t equalised yet) and it wastes energy running near-continuously to hold a number tighter than the room actually needs.
For a basement, a 5–15 %RH swing is completely fine. Precision isn’t the goal here — not restarting the compressor every thirty seconds is the goal.
The fix, in one sentence
Replace the built-in control with a wide, adjustable deadband (turn on at 60 %RH, off at 50 %, hold in between) plus an anti-short-cycle minimum-off timer so the compressor gets a guaranteed rest between cycles. Fewer starts, less wear, lower energy use.
Why two boxes
SHT41 ──I²C──▶ ESP32 ──Wi-Fi──▶ Sonoff S31 ──mains──▶ Dehumidifier
humidity Box A Box B
reads RH, switches the load,
runs the logic enforces compressor limits
Box A — a Wemos ESP32 with a Sensirion SHT41 humidity sensor — is the brain. It reads the room, runs the deadband and the timers, and decides whether the dehumidifier should be running. It is low-voltage only and never goes near mains.
Box B — a Sonoff S31, an ETL-listed smart plug — does the actual 120 VAC switching. That’s the whole reason for splitting it: all the mains wiring, fusing, and creepage distance lives inside a certified appliance. I flash it with new firmware, but I never open its high-voltage side or wire an outlet.
The S31 specifically because it hits every requirement at once: it’s ETL-listed, so the mains engineering is already done and certified; the ESP module inside is reachable through a header for reflashing without cutting into or otherwise destructively modifying the case; and it’s cheap enough that a certified, relay-switched, ESP-controlled outlet costs less than sourcing a bare relay and enclosure separately.
The two boxes talk to each other over plain HTTP on the house Wi-Fi. No Home
Assistant, no MQTT, no broker, no cloud. The controller POSTs
/switch/relay/turn_on (or turn_off) to the plug every 20 seconds — not just
on a change, so if the plug reboots it re-syncs within one cycle. Each box also
raises its own Wi-Fi hotspot and captive portal on first boot, so the pair can
be handed to someone else and onboarded onto a network I’ve never seen.
The guard rails
There are two versions of each config. The simple pair is one self-contained file each — deadband, min-off timer, and nothing else — meant for reading and explaining. The full pair adds the parts that matter when this is switching a real compressor unattended:
- Compressor cool-off on the plug. After the relay opens, the plug refuses any turn-on for a configurable window (default 5 min). This lives on the plug, not the controller, and the controller cannot override it. If the brain does something stupid, the mains side still protects the compressor.
- Power-on delay. An ESP with no synced clock can’t measure how long it was unpowered. So after the plug itself loses power, it assumes the worst and holds off the first turn-on for a few minutes — with a clearly-labelled “force” button on the plug’s own page for the “it’s been off all night, just start it” case.
- Stale-command watchdog. If the plug hasn’t heard from the controller in 15 minutes, it forces the relay off. A dehumidifier stuck on because the brain died is exactly the failure the mains side has to catch on its own.
- Dead-sensor lockout, HTTP Basic auth on the relay endpoint, and a
human-readable last-command status (
OK/AUTH FAILED/UNREACHABLE/HTTP ERROR n) on the controller’s page.
The web UIs
Both boxes run ESPHome’s built-in web server, with the live log streaming alongside the entity table. IPs and MACs blurred out below, but everything else is real: the plug’s cool-off/power-on-delay/watchdog fields, and the controller’s deadband and humidity reading.


Load and relay rating — the honest caveat
The S31’s relay is rated for a resistive load. A dehumidifier is a compressor (motor) load, and motor loads are harder on relay contacts in two ways: the inrush/locked-rotor current at start is several times the running current, and relay makers publish a lower rating for motor loads than the headline resistive figure. Before wiring a real unit through it: read the dehumidifier’s nameplate running current, check it against the S31’s label, and if it’s anywhere near the limit, drive a properly-rated contactor from the S31 instead. The anti-short-cycle timer helps here too — fewer make/break cycles under load means slower contact erosion.
How it’s verified
- CI compiles all four configs against the current ESPHome release on every push, and again on a weekly schedule so a breaking release shows up as a red X instead of an email from a stranger.
- The full pair was bench-tested end to end on real hardware (bare S31, no load): deadband both directions, the full cool-off cycle, the force button, and the stale-command watchdog (I unplugged the controller and watched the relay drop). The test log records what passed and what wasn’t covered.
Getting it
The repo has all four configs and a full README. Release v1.0.0 has pre-built firmware — flash it over serial, or straight from a browser with the included ESP Web Tools manifest. Bring your own Wi-Fi Sonoff S31 (or S31 Lite) and an ESP32 with an SHT4x sensor.