I’m not a huge Blueprint fan, so I made this pyscript.
The script allows you to define a key/value list of switch names with lights to control for the given switch. It handles on/off and brightness controls and has some configuration variables for you to tweak as desired.
Feedback welcome. ![]()
# Zigbee2MQTT - Hue Dimmer Switch Control
# - Listens to MQTT messages on zigbee2mqtt/<device>
# - Maps switch device names to target light entities using SWITCH_TO_LIGHTS dictionary
# - Handles on/off presses and brightness up/down (press for small steps, hold for large steps)
# - Only adjusts brightness when lights are already on
# - Uses action field from Z2M payloads: on_press, off_press, up_press, down_press, up_hold, down_hold
# logger.set_level(
# **{"custom_components.pyscript.file.zigbee2mqtt_hue_switch_control": "debug"}
# )
# Configuration: Map switch device names to target light entities
# Key: Switch name (from MQTT)
# Value: List of light entity IDs that this switch controls.
SWITCH_TO_LIGHTS = {
"Office Hue Switch": [
"light.office_nano_bulb_01",
"light.office_nano_bulb_02",
"light.office_hue_lightstrip",
],
"Living Room Hue Switch": ["light.magic_areas_light_groups_living_room_all_lights"],
}
# Brightness settings
BRIGHTNESS_MIN = 10 # Minimum brightness to ensure visibility
BRIGHTNESS_MAX = 255
BRIGHTNESS_STEP = 30 # ~12% of 255, for press actions
BRIGHTNESS_HOLD_STEP = 56 # ~22% of 255, for hold actions
DEFAULT_TRANSITION = 0.2 # Transition time in seconds
# Clamp brightness to valid range
def clamp_brightness(value: int) -> int:
return max(BRIGHTNESS_MIN, min(int(value), BRIGHTNESS_MAX))
# Returns brightness of first lit entity, or None if no lights are on
def get_current_brightness(target_lights: list[str]) -> int | None:
for entity in target_lights:
if state.get(entity) == "on":
brightness = state.get(f"{entity}.brightness")
if brightness is not None:
return int(brightness)
return None
@mqtt_trigger("zigbee2mqtt/+")
def on_z2m_event(topic=None, payload=None, payload_obj=None, **kwargs):
log.debug(f"Z2M Event - Topic: {topic}")
if not topic or not topic.startswith("zigbee2mqtt/"):
return
device_name = topic.split("/", 1)[1]
# Look up target lights from configuration mapping
target_lights = SWITCH_TO_LIGHTS.get(device_name)
if target_lights is None:
log.debug(f"No mapping found for switch '{device_name}', skipping")
return
log.debug(f"TARGETING: {target_lights}")
# Check if any light entity exists
entity_exists = False
for entity in target_lights:
if state.get(entity):
entity_exists = True
break
if not entity_exists:
log.debug(f"No light entities found in {target_lights}, skipping")
return
# Parse payload
action = (payload_obj or {}).get("action")
if not action:
return
log.debug(f"{device_name} --> Action: {action}")
# Handle on/off press actions
if action == "on_press":
log.debug(f"{device_name} --> TURN ON")
light.turn_on(entity_id=target_lights)
return
elif action == "off_press":
log.debug(f"{device_name} --> TURN OFF")
light.turn_off(entity_id=target_lights)
return
# Handle brightness adjustment actions (press and hold)
elif action in ["up_press", "down_press", "up_hold", "down_hold"]:
current_brightness = get_current_brightness(target_lights)
if current_brightness is None:
return
is_hold = action.endswith("_hold")
direction = "up" if action.startswith("up") else "down"
step = BRIGHTNESS_HOLD_STEP if is_hold else BRIGHTNESS_STEP
delta = step if direction == "up" else -step
new_brightness = clamp_brightness(current_brightness + delta)
# Skip redundant write if no change (for press actions only)
if not is_hold and current_brightness == new_brightness:
return
action_type = "move" if is_hold else "step"
log.debug(
f"{device_name} --> Brightness {direction} {action_type} -> {new_brightness}"
)
light.turn_on(
entity_id=target_lights,
brightness=new_brightness,
transition=DEFAULT_TRANSITION,
)