Prevent spoke triggers

This commit is contained in:
bes-r 2026-05-20 20:42:12 +02:00
parent bc1b58905d
commit 95a2515189

View file

@ -1,13 +1,10 @@
// ============================================================================= // =============================================================================
// Shelly 2PM Detached Script v3.4 // Shelly 2PM Detached Script v3.5
// ============================================================================= // =============================================================================
// Based on v3.3 (stable) // More stable version: counts only btn_down events.
// Fix: supports both state-based and event-based inputs (Gen4 compatible) // This reduces ghost triggers caused by extra btn_up / bounce events.
// Fully English logging & events
// ============================================================================= // =============================================================================
// === CONFIG ==================================================================
var CONFIG = { var CONFIG = {
channels: [ channels: [
{ inputId: 0, outputId: 0 }, { inputId: 0, outputId: 0 },
@ -15,7 +12,9 @@ var CONFIG = {
], ],
SETTLE_MS: 300, SETTLE_MS: 300,
CROSSTALK_MS: 200, CROSSTALK_MS: 300,
COUNT_SIGNAL: "btn_down",
LONG_ENABLED: true, LONG_ENABLED: true,
LONG_MIN_MS: 800, LONG_MIN_MS: 800,
@ -33,8 +32,6 @@ var CONFIG = {
} }
}; };
// === DEVICE INFO =============================================================
var deviceInfo = Shelly.getDeviceInfo(); var deviceInfo = Shelly.getDeviceInfo();
var deviceName = (deviceInfo.name && deviceInfo.name !== "") var deviceName = (deviceInfo.name && deviceInfo.name !== "")
? deviceInfo.name ? deviceInfo.name
@ -47,8 +44,6 @@ for (var ci = 0; ci < CONFIG.channels.length; ci++) {
: "Channel " + (ci + 1); : "Channel " + (ci + 1);
} }
// === STATE ===================================================================
var chState = []; var chState = [];
for (var si = 0; si < CONFIG.channels.length; si++) { for (var si = 0; si < CONFIG.channels.length; si++) {
chState.push({ chState.push({
@ -65,8 +60,6 @@ for (var si = 0; si < CONFIG.channels.length; si++) {
}); });
} }
// === EVENT EMIT ===============================================================
function fireEvent(chIndex, logicalType, toggles) { function fireEvent(chIndex, logicalType, toggles) {
var ch = CONFIG.channels[chIndex]; var ch = CONFIG.channels[chIndex];
var eventType = CONFIG.names[logicalType] || logicalType; var eventType = CONFIG.names[logicalType] || logicalType;
@ -84,7 +77,6 @@ function fireEvent(chIndex, logicalType, toggles) {
toggles: toggles toggles: toggles
}); });
// Offline fallback
if (logicalType === "single") { if (logicalType === "single") {
var w = Shelly.getComponentStatus("wifi"); var w = Shelly.getComponentStatus("wifi");
if (w === null || w.sta_ip === null || w.sta_ip === "") { if (w === null || w.sta_ip === null || w.sta_ip === "") {
@ -94,8 +86,6 @@ function fireEvent(chIndex, logicalType, toggles) {
} }
} }
// === EMERGENCY ===============================================================
function resetEmergency(chIndex) { function resetEmergency(chIndex) {
var s = chState[chIndex]; var s = chState[chIndex];
s.emergencyCount = 0; s.emergencyCount = 0;
@ -109,6 +99,7 @@ function resetEmergency(chIndex) {
function registerEmergency(chIndex, toggles) { function registerEmergency(chIndex, toggles) {
var s = chState[chIndex]; var s = chState[chIndex];
if (toggles < 1) return false; if (toggles < 1) return false;
if (s.emergencyLocked) return true; if (s.emergencyLocked) return true;
@ -142,11 +133,10 @@ function registerEmergency(chIndex, toggles) {
return true; return true;
} }
return false; return false;
} }
// === TOGGLE HANDLING ==========================================================
function emitForToggles(chIndex, toggles) { function emitForToggles(chIndex, toggles) {
if (registerEmergency(chIndex, toggles)) return; if (registerEmergency(chIndex, toggles)) return;
@ -157,8 +147,6 @@ function emitForToggles(chIndex, toggles) {
fireEvent(chIndex, type, toggles); fireEvent(chIndex, type, toggles);
} }
// === BURST ===================================================================
function onBurstEnd(chIndex) { function onBurstEnd(chIndex) {
var s = chState[chIndex]; var s = chState[chIndex];
@ -169,17 +157,19 @@ function onBurstEnd(chIndex) {
var changes = s.stateChanges; var changes = s.stateChanges;
s.stateChanges = 0; s.stateChanges = 0;
var toggles = ((changes + 1) / 2) | 0; // v3.5: one counted btn_down equals one physical toggle
var toggles = changes;
if (toggles < 1) toggles = 1; if (toggles < 1) toggles = 1;
print("[Burst] " + CONFIG.channels[chIndex].name + print("[Burst] " + CONFIG.channels[chIndex].name +
": " + JSON.stringify(changes) + " changes -> " + ": " + JSON.stringify(changes) + " counted events -> " +
JSON.stringify(toggles) + " toggles"); JSON.stringify(toggles) + " toggles");
if (s.waitingForLong) { if (s.waitingForLong) {
var elapsed = Date.now() - s.singleTime; var elapsed = Date.now() - s.singleTime;
s.waitingForLong = false; s.waitingForLong = false;
if (s.longTimer !== null) { if (s.longTimer !== null) {
Timer.clear(s.longTimer); Timer.clear(s.longTimer);
s.longTimer = null; s.longTimer = null;
@ -193,7 +183,11 @@ function onBurstEnd(chIndex) {
} }
fireEvent(chIndex, "single", 1); fireEvent(chIndex, "single", 1);
if (toggles > 0) emitForToggles(chIndex, toggles);
if (toggles > 0) {
emitForToggles(chIndex, toggles);
}
return; return;
} }
@ -206,28 +200,30 @@ function onBurstEnd(chIndex) {
s.longTimer = Timer.set(CONFIG.LONG_MAX_MS, false, function() { s.longTimer = Timer.set(CONFIG.LONG_MAX_MS, false, function() {
s.waitingForLong = false; s.waitingForLong = false;
s.longTimer = null; s.longTimer = null;
print("[Long] Timeout -> single"); print("[Long] Timeout -> single");
emitForToggles(chIndex, 1); emitForToggles(chIndex, 1);
}); });
return; return;
} }
emitForToggles(chIndex, toggles); emitForToggles(chIndex, toggles);
} }
// === EVENT HANDLER ===========================================================
Shelly.addEventHandler(function(event) { Shelly.addEventHandler(function(event) {
if (event.component === undefined) return; if (event.component === undefined) return;
if (event.info === undefined) return; if (event.info === undefined) return;
var chIndex = -1; var chIndex = -1;
for (var i = 0; i < CONFIG.channels.length; i++) { for (var i = 0; i < CONFIG.channels.length; i++) {
if (event.component === ("input:" + CONFIG.channels[i].inputId)) { if (event.component === ("input:" + CONFIG.channels[i].inputId)) {
chIndex = i; chIndex = i;
break; break;
} }
} }
if (chIndex === -1) return; if (chIndex === -1) return;
var signal = null; var signal = null;
@ -249,9 +245,8 @@ Shelly.addEventHandler(function(event) {
if (now - other.lastBurstEnd < CONFIG.CROSSTALK_MS) return; if (now - other.lastBurstEnd < CONFIG.CROSSTALK_MS) return;
} }
// Count only real toggle transitions // Count only selected signal. Default: btn_down.
if (signal === "btn_down" || signal === "btn_up" || event.info.state !== undefined) { if (signal === CONFIG.COUNT_SIGNAL || event.info.state !== undefined) {
s.stateChanges += 1; s.stateChanges += 1;
s.settling = true; s.settling = true;
@ -270,7 +265,12 @@ Shelly.addEventHandler(function(event) {
return; return;
} }
// Show native events for debugging if (signal === "btn_up") {
print("[Ignored] " + CONFIG.channels[chIndex].name +
" = " + JSON.stringify(signal));
return;
}
if (signal === "single_push" || if (signal === "single_push" ||
signal === "double_push" || signal === "double_push" ||
signal === "triple_push" || signal === "triple_push" ||
@ -280,12 +280,11 @@ Shelly.addEventHandler(function(event) {
} }
}); });
// === INIT ==================================================================== print("=== Shelly Detached v3.5 ===");
print("=== Shelly Detached v3.4 ===");
print("Device: " + deviceName); print("Device: " + deviceName);
print("Timing: settle=" + JSON.stringify(CONFIG.SETTLE_MS) + print("Timing: settle=" + JSON.stringify(CONFIG.SETTLE_MS) +
"ms crosstalk=" + JSON.stringify(CONFIG.CROSSTALK_MS) + "ms"); "ms crosstalk=" + JSON.stringify(CONFIG.CROSSTALK_MS) + "ms");
print("Counting only: " + CONFIG.COUNT_SIGNAL);
print("Long: " + (CONFIG.LONG_ENABLED ? "enabled" : "disabled")); print("Long: " + (CONFIG.LONG_ENABLED ? "enabled" : "disabled"));
print("Emergency: " + CONFIG.EMERGENCY_COUNT + " toggles / " + print("Emergency: " + CONFIG.EMERGENCY_COUNT + " toggles / " +
CONFIG.EMERGENCY_WINDOW_MS + "ms"); CONFIG.EMERGENCY_WINDOW_MS + "ms");