UltraPro WFD3001 3-Way Dimmer Switch
Notes
The WFD3001 uses a BK7231N Wi-Fi chip (BK72xx family) with a secondary MCU that controls the actual dimming hardware. Dimming commands are sent over UART to the secondary MCU; the BK7231N does not directly drive the dimmer output.
Flashing: The BK7231N can be flashed via its UART pins using ltchiptool or OpenBK
tools. No disassembly beyond removing the wall plate is required to access the programming
header (1.27 mm pitch).
3-way operation: The switch communicates with a companion 3-way switch unit via an ADC pin that reads the voltage on the traveler wire. When the companion switch changes state, the ADC value crosses a threshold and toggles the primary light.
Upper paddle behavior:
| Action | Result |
|---|---|
| Single click | Toggle light on/off |
| Double click | Set brightness to Preset High |
| Hold | Dim up (5% per 100 ms) |
Lower paddle behavior:
| Action | Result |
|---|---|
| Single click | Toggle light on/off |
| Double click | Set brightness to Preset Low |
| Hold | Dim down (5% per 100 ms, floor at 1%) |
The blue status LED strobes while connecting to Wi-Fi and returns to steady state once connected. It turns off when the light is on (so the switch is visible in the dark when the light is off).
GPIO Pinout
| Pin | Function |
|---|---|
| P6 | Reset button (behind wall plate, active LOW; hold 5 s = factory reset) |
| P7 | Upper paddle button (active LOW) |
| P8 | Lower paddle button (active LOW) |
| P10 | UART RX (from secondary dimmer MCU) |
| P11 | UART TX (to secondary dimmer MCU) |
| P24 | Blue status LED (active HIGH) |
| ADC3 | 3-way companion switch sense (analog voltage divider) |
Basic Configuration
substitutions:
device_name: "ultrapro-wfd3001"
esphome:
name: ${device_name}
friendly_name: "UltraPro WFD3001 Dimmer"
comment: "UltraPro WFD3001 3-Way Dimmer Switch"
name_add_mac_suffix: true
project:
name: UltraPro.WFD3001
version: 1.0.0
on_boot: # Strobe blue LED while connecting to WiFi. wifi on_connect will stop it.
priority: 600
then:
- light.turn_on:
id: blue_led
effect: strobe
bk72xx:
board: generic-bk7231n-qfn32-tuya
logger:
api:
time:
- platform: homeassistant
id: homeassistant_time
ota:
- platform: esphome
wifi:
ap: {}
on_connect: # Stop strobe when WiFi connects; matches current light state
- light.turn_on:
id: blue_led
effect: none
on_disconnect:
- light.turn_on:
id: blue_led
effect: strobe
captive_portal:
# GPIO / Pin Mapping (BK7231N):
# P7 - Toggle button (upper paddle) — active LOW
# P8 - Toggle button (lower paddle) — active LOW
# P6 - Reset button (behind wall plate) — active LOW, hold 5s = factory reset
# P24 - Blue status LED — active HIGH (on when light is OFF)
# P10 - UART RX (to dimmer MCU)
# P11 - UART TX (to dimmer MCU)
# Dimming is controlled via UART serial commands to the secondary MCU.
uart:
baud_rate: 115200
tx_pin: P11
rx_pin: P10
output:
- id: blue_ledout
platform: gpio
pin: P24
- platform: template
id: output_template
type: float
min_power: 0.1
zero_means_zero: true
write_action:
- uart.write: !lambda return {0x65, 0xAA, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, (uint8_t)(state * 255), 0x00, (uint8_t)((277 + (uint8_t)(state * 255)) % 256)};
light:
- platform: binary
id: blue_led
output: blue_ledout
restore_mode: ALWAYS_ON
effects:
- strobe:
- platform: monochromatic
name: "Light"
id: light_primary
output: output_template
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
then:
- light.turn_off: blue_led
on_turn_off:
then:
- light.turn_on: blue_led
binary_sensor:
- platform: gpio
id: button_up
pin:
number: P7
inverted: true
on_multi_click:
- timing: # double click → brightness_preset_high
- ON for at most 350ms
- OFF for at most 350ms
- ON for at most 350ms
- OFF for at least 350ms
then:
- light.turn_on:
id: light_primary
brightness: !lambda 'return id(brightness_preset_high).state / 100.0;'
- timing: # single click → toggle
- ON for at most 350ms
- OFF for at least 350ms
then:
- light.toggle: light_primary
on_press: # hold to dim up
- delay: 0.35s
- while:
condition:
binary_sensor.is_on: button_up
then:
- light.dim_relative:
id: light_primary
relative_brightness: 5%
transition_length: 0.1s
- delay: 0.1s
- platform: gpio
id: button_down
pin:
number: P8
inverted: true
on_multi_click:
- timing: # double click → brightness_preset_low
- ON for at most 350ms
- OFF for at most 350ms
- ON for at most 350ms
- OFF for at least 350ms
then:
- light.turn_on:
id: light_primary
brightness: !lambda 'return id(brightness_preset_low).state / 100.0;'
- timing: # single click → toggle
- ON for at most 350ms
- OFF for at least 350ms
then:
- light.toggle: light_primary
on_press: # hold to dim down (floor at 1% so it never turns off)
- delay: 0.35s
- while:
condition:
binary_sensor.is_on: button_down
then:
if:
condition:
lambda: 'return(id(light_primary).remote_values.get_brightness() < 0.06);'
then:
- light.turn_on:
id: light_primary
brightness: 1%
- delay: 0.1s
else:
- light.dim_relative:
id: light_primary
relative_brightness: -5%
transition_length: 0.1s
- delay: 0.1s
- platform: gpio
id: button_reset
pin:
number: P6
inverted: true
on_press:
- delay: 5s
- if:
condition:
binary_sensor.is_on: button_reset
then:
- button.press: factory_reset_button
button:
- platform: restart
id: restart_button
name: "Restart"
- platform: safe_mode
id: restart_button_safe_mode
name: "Restart (Safe Mode)"
- platform: factory_reset
id: factory_reset_button
name: "Factory Reset"
disabled_by_default: true
entity_category: config
icon: mdi:restart-alert
number:
- platform: template
name: "Brightness Preset High"
id: brightness_preset_high
min_value: 1
max_value: 100
step: 1
initial_value: 100
restore_value: true
optimistic: true
unit_of_measurement: "%"
entity_category: config
icon: mdi:brightness-7
- platform: template
name: "Brightness Preset Low"
id: brightness_preset_low
min_value: 1
max_value: 100
step: 1
initial_value: 30
restore_value: true
optimistic: true
unit_of_measurement: "%"
entity_category: config
icon: mdi:brightness-4
sensor:
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 60s
- platform: uptime
name: "Uptime"
- platform: adc
pin: ADC3
name: "3 Way Sense"
update_interval: 60ms
filters:
- skip_initial: 4
- exponential_moving_average:
alpha: 0.1
send_every: 8
send_first_at: 8
- delta: 0.005
on_value_range:
- below: 0.1350
then:
- light.toggle: light_primary
- above: 0.1600
then:
- light.toggle: light_primary
text_sensor:
- platform: version
name: "ESPHome Version"
- platform: wifi_info
ip_address:
name: "IP Address"
mac_address:
name: "MAC Address"
ssid:
name: "SSID Connected"