// ============================================================================= // Shelly 1PM Detached Script v3.7 // Native click events + custom emergency // ============================================================================= var CONFIG = { channels: [ { inputId: 0, outputId: 0 } ], EMERGENCY_COUNT: 5, EMERGENCY_WINDOW_MS: 2500, names: { single: "single", double: "double", triple: "triple", long: "long", emergency: "emergency" } }; var deviceInfo = Shelly.getDeviceInfo(); var deviceName = (deviceInfo.name && deviceInfo.name !== "") ? deviceInfo.name : deviceInfo.model + " " + deviceInfo.id; for (var ci = 0; ci < CONFIG.channels.length; ci++) { var inputCfg = Shelly.getComponentConfig("input:" + CONFIG.channels[ci].inputId); CONFIG.channels[ci].name = (inputCfg && inputCfg.name && inputCfg.name !== "") ? inputCfg.name : "Channel " + (ci + 1); } var chState = []; for (var si = 0; si < CONFIG.channels.length; si++) { chState.push({ emergencyCount: 0, emergencyTimer: null, emergencyLocked: false }); } function fireEvent(chIndex, logicalType, toggles) { var ch = CONFIG.channels[chIndex]; var eventType = CONFIG.names[logicalType] || logicalType; print(">>> " + ch.name + ": " + eventType + " (" + JSON.stringify(toggles) + " toggles)"); Shelly.emitEvent("toggle_event", { input: ch.inputId, input_name: ch.name, output: ch.outputId, device: deviceName, type: eventType, logical_type: logicalType, toggles: toggles }); } function resetEmergency(chIndex) { var s = chState[chIndex]; s.emergencyCount = 0; s.emergencyLocked = false; if (s.emergencyTimer !== null) { Timer.clear(s.emergencyTimer); s.emergencyTimer = null; } } function registerEmergency(chIndex) { var s = chState[chIndex]; if (s.emergencyLocked) return; if (s.emergencyCount === 0) { s.emergencyTimer = Timer.set(CONFIG.EMERGENCY_WINDOW_MS, false, function() { print("[Emergency] Timeout " + CONFIG.channels[chIndex].name + " after " + JSON.stringify(s.emergencyCount) + " clicks"); resetEmergency(chIndex); }); } s.emergencyCount += 1; print("[Emergency] " + CONFIG.channels[chIndex].name + ": " + JSON.stringify(s.emergencyCount) + "/" + JSON.stringify(CONFIG.EMERGENCY_COUNT)); if (s.emergencyCount >= CONFIG.EMERGENCY_COUNT) { s.emergencyLocked = true; if (s.emergencyTimer !== null) { Timer.clear(s.emergencyTimer); s.emergencyTimer = null; } fireEvent(chIndex, "emergency", s.emergencyCount); Timer.set(1000, false, function() { resetEmergency(chIndex); }); } } Shelly.addEventHandler(function(event) { if (event.component === undefined) return; if (event.info === undefined) return; var chIndex = -1; for (var i = 0; i < CONFIG.channels.length; i++) { if (event.component === ("input:" + CONFIG.channels[i].inputId)) { chIndex = i; break; } } if (chIndex === -1) return; var signal = null; if (event.info.event !== undefined) { signal = event.info.event; } else if (event.info.state !== undefined) { signal = event.info.state; } else { return; } if (signal === "btn_down") { print("[Count] " + CONFIG.channels[chIndex].name + " = btn_down"); registerEmergency(chIndex); return; } if (signal === "btn_up") { print("[Ignored] " + CONFIG.channels[chIndex].name + " = btn_up"); return; } if (signal === "single_push") { fireEvent(chIndex, "single", 1); return; } if (signal === "double_push") { fireEvent(chIndex, "double", 2); return; } if (signal === "triple_push") { fireEvent(chIndex, "triple", 3); return; } if (signal === "long_push") { fireEvent(chIndex, "long", 1); return; } print("[Ignored] " + CONFIG.channels[chIndex].name + " = " + JSON.stringify(signal)); }); print("=== Shelly 1PM Detached v3.7 ==="); print("Device: " + deviceName); print("Mode: native click events + custom emergency"); print("Emergency: " + CONFIG.EMERGENCY_COUNT + " clicks / " + CONFIG.EMERGENCY_WINDOW_MS + "ms"); for (var pi = 0; pi < CONFIG.channels.length; pi++) { var ch = CONFIG.channels[pi]; print(" " + ch.name + " input:" + ch.inputId + " -> output:" + ch.outputId); } print("[Script] Active");