Universal HomeSeer In-Wall Switch Control (Z-Wave JS)
One universal blueprint for every scene-capable HomeSeer in-wall Z-Wave device: switch, dimmer and fan controller. It handles top and bottom paddle taps 1x through 5x plus hold and release, accepts several switches per automation, and can optionally push device configuration (LED behaviour, colours, ramp rates and more) to the switch every time you save the automation, so your settings survive a re-inclusion or a factory reset. I was tired of making tweaks and changes to five different blueprints because I own different pieces of hardware. Also, some of the blueprints that are out there for these specific devices no longer work properly. So I was maintaining and building my own anyway. I should share this for anyone else using these.
Supported models
top │ ▲ 001 │ taps 1x–5x · hold · release
bottom │ ▼ 002 │ taps 1x–5x · hold · release(1x–3x on the 100 series)
| Model | Type | Taps | Hold / release | Controllable status LEDs |
|---|---|---|---|---|
| HS-WD100+ | Dimmer | 1x–3x | ✓ | – |
| HS-WS100+ | Switch | 1x–3x | ✓ | – |
| HS-WD200+ | Dimmer | 1x–5x | ✓ | 7 |
| HS-WS200+ | Switch | 1x–5x | ✓ | 1 |
| HS-WX300 | Dimmer or switch | 1x–5x | ✓ | 7 |
| WS300 | Switch | 1x–5x | ✓ | 1 |
| HS-FC200+ | Fan controller | 1x–5x | ✓ | 4 |
The HS-WX300 sends identical scene events whether it was set up as a dimmer or an on/off switch, so both modes work with no extra configuration. Tap slots a model does not report (4x/5x on the 100 series) are simply never triggered; leaving them empty is harmless.
Configuration the blueprint can manage, per model
| Setting (parameter) | WD100+ | WS100+ | WD200+ | WS200+ | WX300 | WS300 | FC200+ |
|---|---|---|---|---|---|---|---|
| LED indicator operation (3) | – | ✓ ¹ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Load LED colour (14) | – | – | ✓ | ✓ | ✓ | ✓ ² | ✓ |
| LED brightness (34) | – | – | – | – | ✓ ³ | ✓ | – |
| Dimming threshold (5) | – | – | ✓ ⁴ | – | ✓ | – | – |
| Ramp rate, Z-Wave (11) | – | – | ✓ | – | ✓ | – | – |
| Ramp rate, paddle (12) | – | – | ✓ | – | ✓ | – | – |
| Default brightness (36) | – | – | – | – | ✓ ³ | – | – |
| Wire mode (32) | – | – | – | – | ✓ | – | – |
¹ WS100+ has its own three-option set: on when load is off, on when load is on, always off.
² WS300 adds Pink, Orange, Teal and Custom to the colour list.
³ Requires WX300 firmware 1.13 or later.
⁴ Requires WD200+ firmware 5.14 or later.
Every setting defaults to leave unchanged and is only written if you change it. Values are sent as labels rather than raw numbers, so parameter 3, whose numeric meaning is reversed between the 200 series and the 300 series, lands correctly on every model. Settings a model does not have are skipped.
Features
- All fourteen scene slots: top and bottom paddle, 1 to 5 taps, hold, release
- Multiple switches in one automation
- Device configuration pushed automatically when the automation is saved or Home Assistant restarts (can be turned off in Advanced)
mode: queued, so a fast second press is not silently dropped while the first is still running- Optional diagnostics: per-event logbook entries and a summary notification after each configuration push
- An “any scene event” hook that runs before the specific tap actions, with
paddleandpressin scope, for people who want to route events themselves
Requirements
- Home Assistant 2024.10 or newer
- The Z-Wave JS integration (listed as Z-Wave under Settings > Devices & services)
- Central Scene enabled on the switch: parameter 6 set to 0. The 200 series needs firmware 5.12 or later for parameter 6 to exist; the 100 series has no parameter 6 and always reports scenes
Troubleshooting
- Nothing fires at all: Developer Tools > Events, listen to
zwave_js_value_notification, tap a paddle. No event means the problem is upstream of the blueprint: check parameter 6 on the device, and check the device is provided by Z-Wave JS. - The device picker is empty: your Home Assistant reports a manufacturer or model string that does not match the filter. Edit the blueprint and delete the
filter:block under the device selector, leavingmultiple: true, then pick the switch by hand. A reply here with what your device page shows for manufacturer and model would help me extend the filter. - A configuration value did not take: the model may not have that parameter (see the matrix above), or the firmware gate is not met. Turn on Diagnostics and push again; the notification lists what was attempted, and the device’s Configure page shows what landed.
Notes
- Configuration is re-applied whenever any automation reloads. That makes this blueprint the authority on the parameters you set through it: a value changed on the device’s Configure page will be reverted at the next reload. Set the value here instead, or turn off “Apply configuration on reload” in Advanced and manage parameters on the device page.
- Dimmer versus switch mode on the HS-WX300 is not a Z-Wave parameter. It is set with the button on the metal yoke, or tap-tap-tap-press-and-hold on the top paddle, before including the switch.
- I own a subset of these models. The full matrix comes from the Z-Wave JS device database. If you run one I have not, especially the HS-FC200+ or a WS300, a confirmation or a bug report is welcome.
- Supersedes the two per-model community blueprints:
- HS-WD200+ ZWave-JS - Homeseer HS-WD200+ Dimmer
- HS-WX300 Z-Wave JS - HomeSeer HS-WX300+ Dimmer
- Config-on-reload pattern borrowed from:
Changelog
- v0.4 first public release: all seven in-wall models, fourteen scene slots, eight managed configuration parameters
---
# BLP-homeseer-inwall-control.yaml
# Blueprint name in the HA UI: "Universal HomeSeer In-Wall Switch Control"
# v0.4 2026-08-21
#
# TWO TRIGGER PATHS, ONE AUTOMATION:
# scene zwave_js_value_notification -> run the tap actions
# apply_config automation_reloaded or HA start -> push config parameters
#
# CONFIG IS WRITTEN ON EVERY AUTOMATION RELOAD, NOT JUST THIS ONE.
# "automation_reloaded" is a global event. Any automation reload re-pushes
# these parameters to every device selected here. Consequences:
# - This blueprint becomes the authority. A value you change on the device's
# Configure page is silently reverted at the next reload.
# - Traffic is (settings you changed) x (devices selected) Z-Wave writes per
# reload. Settings left at their no-op default write nothing, so an
# instance that configures nothing costs nothing.
# - Turn it off entirely with "Apply configuration on reload" in Advanced.
#
# WHY VALUES ARE WORDS, NOT NUMBERS.
# zwave_js.set_config_parameter accepts the state LABEL in place of the integer
# and resolves it against that device's own config file. Parameter 3 is
# INVERTED between models:
# HS-WD200+ / HS-WS200+ / HS-FC200+ : 0 = On when load is off, 1 = Off when load is off
# HS-WX300 / WS300 : 0 = Off when load is off, 1 = On when load is off
# Writing integer 1 to a mixed set gives the opposite result on half of them.
# Writing "Off when load is off" is correct on all of them. Labels are CASE
# SENSITIVE. Parameters are targeted by NUMBER because their names differ per
# model (14 is "Load Status LED Color" on the WD200+, "LED Normal Color" on the
# WS300).
#
# WHICH CONFIG SETTINGS APPLY TO WHICH MODEL (blueprints cannot hide inputs
# per device, so this table is the substitute):
#
# Setting param WD100+ WS100+ WD200+ WS200+ WX300 WS300 FC200+
# LED indicator 3 - * y y y y y
# Load LED colour 14 - - y y y y(+4) y
# LED brightness 34 - - - - y(fw) y -
# Dimming threshold 5 - - y(fw) - y - -
# Ramp rate (Z-Wave) 11 - - y - y - -
# Ramp rate (manual) 12 - - y - y - -
# Default brightness 36 - - - - y(fw) - -
# Wire mode 32 - - - - y - -
#
# * WS100+ offers "On when load is off", "On when load is on", "Always off".
# Only the first overlaps with the other models.
# fw WD200+ dimming threshold needs firmware 5.14+. WX300 brightness and
# default brightness need firmware 1.13+.
# +4 WS300 adds Orange, Teal, Pink and Custom to the colour list.
#
# A parameter the model does not have is skipped by continue_on_error rather
# than aborting the run, so failures are SILENT.
#
# DIMMER VERSUS SWITCH MODE IS NOT A PARAMETER. On the WX300 it is the small
# button on the metal yoke, or tap-tap-tap-press-and-hold on the top paddle,
# and it must be done BEFORE Z-Wave inclusion. Parameter 37 ("Control Load with
# Paddle") is a different thing: it disables the relay so the paddle only sends
# scenes. Not exposed here.
#
# 4x and 5x taps exist only on the 200 and 300 series. On HS-WD100+ / HS-WS100+
# those inputs are simply never triggered; leaving them empty is harmless.
#
# Config-on-reload pattern taken from:
# https://github.com/rwalker777/Zen32-HA-Blueprint/blob/26ad09e0b45a8ebb1901a697bfd9d6f21244b2e2/ZEN32-control-track.yaml
blueprint:
name: Universal HomeSeer In-Wall Switch Control
description: >-
One universal blueprint for every scene-capable HomeSeer in-wall Z-Wave
device: HS-WD100+, HS-WS100+, HS-WD200+, HS-WS200+, HS-WX300, WS300 and
HS-FC200+. Handles top and bottom paddle taps 1x through 5x, plus hold and
release, and pushes device configuration parameters when the automation is
saved.
Tap counts by generation: 100 series reports 1x, 2x, 3x, hold and release.
200 and 300 series add 4x and 5x. A WX300 reports the same scene events
whether it was configured as a dimmer or as an on/off switch before
inclusion, so no separate handling is needed for switch mode.
Configuration settings default to no-op, so only what you change is
written. Not every setting exists on every model; see the table in the file
header. Settings the device does not have are skipped silently.
If no tap ever fires, check Z-Wave parameter 6 (Central Scene Commands) on
the device. Value 0 enables scene reporting with a short load delay; value 1
disables it. Parameter 6 requires firmware 5.12 or later on the HS-WD200+
and HS-WS200+, and does not exist on the 100 series.
domain: automation
homeassistant:
min_version: 2024.10.0
input:
device_section:
name: Devices
icon: mdi:light-switch
description: >-
Pick one or more HomeSeer switches. Every selected device runs the same
actions and receives the same configuration. Use separate automations
when devices need different actions, or when they are different models
with different configuration needs.
input:
homeseer_devices:
name: HomeSeer device(s)
selector:
device:
multiple: true
filter:
- manufacturer: HomeSeer Technologies
model: HS-WD100+
- manufacturer: HomeSeer Technologies
model: HS-WS100+
- manufacturer: HomeSeer Technologies
model: HS-WD200+
- manufacturer: HomeSeer Technologies
model: HS-WS200+
- manufacturer: HomeSeer Technologies
model: HS-WX300
- manufacturer: HomeSeer Technologies
model: WS300
- manufacturer: HomeSeer Technologies
model: HS-FC200+
config_section:
name: Device configuration
icon: mdi:cog
collapsed: true
description: >-
Written to the switch when this automation is saved or reloaded, and at
Home Assistant start. Leave a setting at its default and it is never
written.
input:
led_indicator:
name: LED indicator operation (parameter 3)
description: >-
Whether the paddle LED lights when the load is off or when it is on.
"On when load is on" and "Always off" exist only on the HS-WS100+.
default: Leave unchanged
selector:
select:
mode: dropdown
options:
- Leave unchanged
- Off when load is off
- On when load is off
- On when load is on
- Always off
load_led_color:
name: Load LED colour (parameter 14)
description: >-
The base colour the LED shows while indicating load state. Pink,
Orange, Teal and Custom exist only on the WS300.
default: Leave unchanged
selector:
select:
mode: dropdown
options:
- Leave unchanged
- White
- Red
- Green
- Blue
- Magenta
- Yellow
- Cyan
- Pink
- Orange
- Teal
- Custom
led_brightness:
name: LED brightness (parameter 34)
description: >-
0 to 6. HS-WX300 (firmware 1.13+) and WS300 only. -1 leaves it alone.
default: -1
selector:
number:
min: -1
max: 6
step: 1
mode: box
dimming_threshold:
name: Dimming threshold (parameter 5)
description: >-
Minimum dim level, 1 to 14. Dimmers only. HS-WD200+ needs firmware
5.14 or later. -1 leaves it alone.
default: -1
selector:
number:
min: -1
max: 14
step: 1
mode: box
ramp_rate_zwave:
name: Ramp rate, Z-Wave commands (parameter 11)
description: >-
Seconds to reach target level when commanded over Z-Wave, 0 to 90.
Dimmers only. Factory default is 3. -1 leaves it alone.
default: -1
selector:
number:
min: -1
max: 90
step: 1
unit_of_measurement: s
mode: box
ramp_rate_manual:
name: Ramp rate, local paddle (parameter 12)
description: >-
Seconds to reach target level when operated at the switch, 0 to 90.
Dimmers only. Factory default is 3. -1 leaves it alone.
default: -1
selector:
number:
min: -1
max: 90
step: 1
unit_of_measurement: s
mode: box
default_brightness:
name: Default brightness on manual turn-on (parameter 36)
description: >-
0 to 99. 0 means resume the last non-zero brightness. HS-WX300
(firmware 1.13+) only. -1 leaves it alone.
default: -1
selector:
number:
min: -1
max: 99
step: 1
unit_of_measurement: "%"
mode: box
wire_mode:
name: Wire mode (parameter 32)
description: >-
HS-WX300 only. Match this to how the switch is actually wired.
Setting it wrong on a dimmer causes flicker or refusal to dim.
default: Leave unchanged
selector:
select:
mode: dropdown
options:
- Leave unchanged
- 3 wire mode (Neutral, line & load)
- 2 wire mode (Line & load)
single_tap_section:
name: Single tap
icon: mdi:gesture-tap
collapsed: true
description: >-
Fires on every normal paddle press, alongside the switch controlling its
own load. Usually left empty unless the load relay is disabled
(parameter 37 on WX300 and WS300).
input:
up_1x:
name: Top paddle, 1 tap
default: []
selector:
action:
dn_1x:
name: Bottom paddle, 1 tap
default: []
selector:
action:
double_tap_section:
name: Double tap
icon: mdi:gesture-double-tap
collapsed: true
input:
up_2x:
name: Top paddle, 2 taps
default: []
selector:
action:
dn_2x:
name: Bottom paddle, 2 taps
default: []
selector:
action:
triple_tap_section:
name: Triple tap
icon: mdi:gesture-tap-button
collapsed: true
input:
up_3x:
name: Top paddle, 3 taps
default: []
selector:
action:
dn_3x:
name: Bottom paddle, 3 taps
default: []
selector:
action:
quad_tap_section:
name: Quadruple tap (200 and 300 series only)
icon: mdi:gesture-tap-button
collapsed: true
description: >-
Not reported by HS-WD100+ or HS-WS100+. Leave empty for those.
input:
up_4x:
name: Top paddle, 4 taps
default: []
selector:
action:
dn_4x:
name: Bottom paddle, 4 taps
default: []
selector:
action:
quint_tap_section:
name: Quintuple tap (200 and 300 series only)
icon: mdi:gesture-tap-button
collapsed: true
description: >-
Not reported by HS-WD100+ or HS-WS100+. Leave empty for those.
input:
up_5x:
name: Top paddle, 5 taps
default: []
selector:
action:
dn_5x:
name: Bottom paddle, 5 taps
default: []
selector:
action:
hold_section:
name: Hold and release
icon: mdi:gesture-tap-hold
collapsed: true
description: >-
On a dimmer, holding also ramps the local load. Release fires when the
paddle is let go.
input:
up_hold:
name: Top paddle held
default: []
selector:
action:
dn_hold:
name: Bottom paddle held
default: []
selector:
action:
up_release:
name: Top paddle released
default: []
selector:
action:
dn_release:
name: Bottom paddle released
default: []
selector:
action:
advanced_section:
name: Advanced
icon: mdi:tune
collapsed: true
input:
any_event:
name: Any scene event
description: >-
Runs on every Central Scene event from the selected devices, before
the specific tap actions. The variables paddle and press are in
scope.
default: []
selector:
action:
apply_config_on_reload:
name: Apply configuration on reload
description: >-
Push the Device configuration settings whenever any automation is
reloaded and at Home Assistant start. Turn off to make this a
scene-only automation and manage parameters on the device page.
default: true
selector:
boolean:
debug_logbook:
name: Diagnostics
description: >-
Writes each paddle event to the logbook, and posts a notification
after a configuration push listing what was attempted. Leave off in
normal use.
default: false
selector:
boolean:
mode: queued
max: 25
max_exceeded: silent
variables:
device_ids: !input homeseer_devices
debug: !input debug_logbook
apply_config: !input apply_config_on_reload
v_led_indicator: !input led_indicator
v_load_led_color: !input load_led_color
v_led_brightness: !input led_brightness
v_dimming_threshold: !input dimming_threshold
v_ramp_rate_zwave: !input ramp_rate_zwave
v_ramp_rate_manual: !input ramp_rate_manual
v_default_brightness: !input default_brightness
v_wire_mode: !input wire_mode
triggers:
- trigger: event
event_type: zwave_js_value_notification
id: scene
- trigger: event
event_type: automation_reloaded
id: apply_config
- trigger: homeassistant
event: start
id: apply_config
conditions: []
actions:
- choose:
- alias: Push device configuration parameters
conditions:
- condition: trigger
id: apply_config
- condition: template
value_template: "{{ apply_config }}"
sequence:
- variables:
pending: >-
{{ [
{'p': 3, 'v': v_led_indicator},
{'p': 14, 'v': v_load_led_color},
{'p': 32, 'v': v_wire_mode},
{'p': 34, 'v': v_led_brightness},
{'p': 5, 'v': v_dimming_threshold},
{'p': 11, 'v': v_ramp_rate_zwave},
{'p': 12, 'v': v_ramp_rate_manual},
{'p': 36, 'v': v_default_brightness}
] | rejectattr('v', 'in', ['Leave unchanged', -1]) | list }}
- variables:
writes: >-
{%- set ns = namespace(out=[]) -%}
{%- for d in device_ids -%}
{%- for w in pending -%}
{%- set ns.out = ns.out + [{'d': d, 'p': w.p, 'v': w.v}] -%}
{%- endfor -%}
{%- endfor -%}
{{ ns.out }}
- repeat:
for_each: "{{ writes }}"
sequence:
- action: zwave_js.set_config_parameter
continue_on_error: true
target:
device_id: "{{ repeat.item.d }}"
data:
parameter: "{{ repeat.item.p }}"
value: "{{ repeat.item.v }}"
- if:
- condition: template
value_template: "{{ debug and pending | count > 0 }}"
then:
- action: persistent_notification.create
data:
title: HomeSeer configuration pushed
message: >-
{{ writes | count }} write(s) attempted across
{{ device_ids | count }} device(s). Parameters the model does
not have were skipped silently. Verify at Settings > Devices
& services > the device > Configure.
{% for w in pending %}Parameter {{ w.p }} = {{ w.v }}
{% endfor %}
- alias: Handle a paddle scene event
conditions:
- condition: trigger
id: scene
- condition: template
value_template: >-
{{ trigger.event.data.device_id in device_ids
and trigger.event.data.command_class_name == 'Central Scene'
and trigger.event.data.property_key_name in ['001', '002'] }}
sequence:
- variables:
paddle: "{{ trigger.event.data.property_key_name }}"
press: "{{ trigger.event.data.value }}"
- if:
- condition: template
value_template: "{{ debug }}"
then:
- action: logbook.log
data:
name: HomeSeer scene
message: >-
{{ device_attr(trigger.event.data.device_id, 'name') }}
paddle {{ '001 (top)' if paddle == '001' else '002 (bottom)' }}
event {{ press }}
- sequence: !input any_event
- choose:
- conditions: "{{ paddle == '001' and press == 'KeyPressed' }}"
sequence: !input up_1x
- conditions: "{{ paddle == '002' and press == 'KeyPressed' }}"
sequence: !input dn_1x
- conditions: "{{ paddle == '001' and press == 'KeyPressed2x' }}"
sequence: !input up_2x
- conditions: "{{ paddle == '002' and press == 'KeyPressed2x' }}"
sequence: !input dn_2x
- conditions: "{{ paddle == '001' and press == 'KeyPressed3x' }}"
sequence: !input up_3x
- conditions: "{{ paddle == '002' and press == 'KeyPressed3x' }}"
sequence: !input dn_3x
- conditions: "{{ paddle == '001' and press == 'KeyPressed4x' }}"
sequence: !input up_4x
- conditions: "{{ paddle == '002' and press == 'KeyPressed4x' }}"
sequence: !input dn_4x
- conditions: "{{ paddle == '001' and press == 'KeyPressed5x' }}"
sequence: !input up_5x
- conditions: "{{ paddle == '002' and press == 'KeyPressed5x' }}"
sequence: !input dn_5x
- conditions: "{{ paddle == '001' and press == 'KeyHeldDown' }}"
sequence: !input up_hold
- conditions: "{{ paddle == '002' and press == 'KeyHeldDown' }}"
sequence: !input dn_hold
- conditions: "{{ paddle == '001' and press == 'KeyReleased' }}"
sequence: !input up_release
- conditions: "{{ paddle == '002' and press == 'KeyReleased' }}"
sequence: !input dn_release
