Shelly Gen4 Button and Fallback + Home Assistant Webhook Integration Guide (with the help of ChatGPT
)
UPDATE 9/6 - to not toggle light in HA automation if the relay was OFF and turned ON by Shelly Script meaning light is already ON
This guide helps you integrate your Shelly Gen4 button presses with Home Assistant via webhook, including a fallback relay toggle if HA does not acknowledge the event.
Overview
This guide shows how to set up a Shelly Gen4 button with Home Assistant to trigger actions on a single press.
- When the button is pressed once, the Shelly device ensures its relay is turned ON before sending a webhook event to Home Assistant.
- If Home Assistant does not acknowledge the event within 2 seconds, the Shelly device will toggle the relay locally as a fallback, ensuring the command is executed.
- The Shelly device communicates with Home Assistant over a local webhook, sending the device ID and the event type (
single_push
). - Home Assistant processes this webhook to toggle lights or trigger automations based on the specific Shelly device.
Please Note: I have done some testing but not sure if it will work for others?
Step 1: Shelly Gen4 Device Script
Upload this JavaScript code to your Shelly Gen4 device via the Shelly web UI under Scripts.
// ===== CONFIGURE PER DEVICE =====
let DEVICE_ID = "YOUR_DEVICE_ID"; // Change this for each device
let HA_URL = "http://YOUR_HA_IP:8123";
let WEBHOOK_URL = HA_URL + "/api/webhook/shelly_button";
let FALLBACK_TIMEOUT_MS = 2000;
let fallbackTimer = null;
let inProgress = false;
let relayWasOnBeforePress = false;
// Toggle relay
function toggleRelay() {
print("Toggling relay locally.");
Shelly.call("Switch.Toggle", { id: 0 });
}
// Set relay OFF
function turnRelayOff() {
print("Reverting relay to OFF.");
Shelly.call("Switch.Set", { id: 0, on: false });
}
// Set relay ON
function turnRelayOn(callback) {
Shelly.call("Switch.GetStatus", { id: 0 }, function (res) {
relayWasOnBeforePress = res && res.output === true;
if (!relayWasOnBeforePress) {
print("Relay is OFF. Turning it ON...");
Shelly.call("Switch.Set", { id: 0, on: true }, function () {
print("Relay turned ON.");
if (callback) callback();
});
} else {
print("Relay already ON.");
if (callback) callback();
}
});
}
// Cancel fallback timer
function cancelFallback() {
if (fallbackTimer !== null) {
Timer.clear(fallbackTimer);
fallbackTimer = null;
print("Fallback cancelled: HA responded.");
}
}
// Fallback logic
function fallbackRelay() {
fallbackTimer = null;
if (relayWasOnBeforePress) {
print("No HA response. Relay was ON before press — turning it OFF.");
turnRelayOff();
} else {
print("No HA response. Relay was OFF before press — leaving it ON.");
}
inProgress = false;
}
// Button event handler
Shelly.addEventHandler(function (ev) {
if (ev.component !== "input:0") return;
let action = ev.info && ev.info.event;
if (action !== "single_push") return;
if (inProgress) {
print("Ignoring press — still processing last action.");
return;
}
inProgress = true;
print("Detected single push on device " + DEVICE_ID);
turnRelayOn(function () {
fallbackTimer = Timer.set(FALLBACK_TIMEOUT_MS, false, fallbackRelay);
Shelly.call("HTTP.POST", {
url: WEBHOOK_URL,
body: JSON.stringify({ id: DEVICE_ID, event: action }),
headers: { "Content-Type": "application/json" }
}, function (res, err) {
if (res && res.code === 200) {
cancelFallback();
print("HA responded successfully.");
inProgress = false;
} else {
print("HA failed to respond or returned error: " + JSON.stringify(err));
// fallbackRelay() will run if needed
}
});
});
});
Step 2: Home Assistant Automation
Add this automation to your Home Assistant (e.g., automations.yaml
):
alias: Shelly Button Triggers
description: ""
trigger:
- platform: webhook
webhook_id: shelly_button
allowed_methods:
- POST
- PUT
local_only: true
action:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.json.id == 'device_id' }}"
- condition: template
value_template: >
{% set last_changed = states.switch.wir_relay.last_changed %}
{% set now = now() %}
{{ states('switch.wir_relay') != 'on' or (now - last_changed).total_seconds() >= 3 }}
sequence:
- service: light.toggle
target:
entity_id: light.wir
- conditions:
- condition: template
value_template: "{{ trigger.json.id == 'another_device_id' }}"
sequence:
- service: light.toggle
target:
entity_id: light.bedroom_lights
default:
- service: notify.mobile_app_yourphone
data:
message: "Shelly button triggered from unknown device: {{ trigger.json.id }}"
- delay: "00:00:01"
- service: rest_command.shelly_acknowledge
Final Notes
-
Replace all placeholders:
YOUR_DEVICE_ID
→ Shelly Gen4 unique device IDYOUR_HA_IP
→ Home Assistant IP or hostnamelight.your_light
,scene.your_scene
,cover.your_cover
→ Your HA entity IDs
-
The fallback toggle triggers if HA does not respond within 2 seconds (configurable in the script).
-
Make sure your Shelly and Home Assistant are reachable on the same network.