[BETA] Namron Simplify 1/2/3-Way Rocker (4512793) - Full mapping with Hold/Release

A new custom driver that provides support for Namron (Sunricher) wall battery switch (scene)

  • manufacturer: NAMRON AS
  • model: 4512793
  • 1, 2 or 3 rocker switches (dip switch configured)

@kkossev , this is mostly "your work" and me adjusting ... Feel free to fit into your builds no questions asked :slight_smile:

/**
 * =======================================================================================
 * Namron Simplify - 4512793 Driver (1, 2, or 3-Way Rocker)
 * Multi-tap support.
 * =======================================================================================
 */

metadata {
    definition (name: "Namron Simplify - 4512793", namespace: "custom", author: "oalund") {
        capability "Configuration"
        capability "PushableButton"
        capability "DoubleTapableButton" 
        capability "HoldableButton"      
        capability "ReleasableButton"  
        capability "Battery"
        capability "Refresh"
        capability "Initialize"

        fingerprint profileId: "0104", endpointId: "01", inClusters: "0000,0001,0003,E004", outClusters: "0003,0019", model: "Fallback", manufacturer: "Namron"
    }

    preferences {
        input(name: 'deviceModel', type: 'enum', title: '<b>Button Layout (DIP Switch)</b>', 
              options: ["2":"1-Way (1 Rocker)", "4":"2-Way (2 Rockers)", "6":"3-Way (3 Rockers)"], defaultValue: "4")
        input(name: 'logEnable', type: 'bool', title: '<b>Enable debug logging</b>', defaultValue: true)
        input(name: 'txtEnable', type: 'bool', title: '<b>Enable info text logging</b>', defaultValue: true)
    }
}

def parse(String description) {
    if (logEnable) log.debug "Namron received: ${description}"
    def descMap = zigbee.parseDescriptionAsMap(description)
    
    // 1. BUTTON LOGIC (Cluster E004)
    if (descMap.clusterId == "E004" || descMap.clusterInt == 57348) {
        if (descMap.data != null && descMap.data.size() >= 2) {
            String btnHex = descMap.data[0] // Knappen (01, 02, 03, 04...)
            String cmdHex = descMap.data[1] // Handlingen (01=Push, 08=Double, 02=Hold, 04=Rel)
            
            int btnNum = 0
            
            switch(btnHex) {
                case "01": btnNum = 1; break; 
                case "02": btnNum = 2; break; 
                case "03": btnNum = 3; break; 
                case "04": btnNum = 4; break; 
                case "05": btnNum = 5; break; 
                case "06": btnNum = 6; break; 
            }
            
            int maxButtons = (deviceModel ?: "6") as Integer
            
            if (btnNum > 0 && btnNum <= maxButtons) {
                switch(cmdHex) {
                    case "01": // PUSH
                        if (txtEnable) log.info "${device.displayName}: Button ${btnNum} pushed"
                        sendEvent(name: "pushed", value: btnNum, isStateChange: true)
                        break
                    case "08": // DOUBLE TAP
                        if (txtEnable) log.info "${device.displayName}: Button ${btnNum} doubleTapped"
                        sendEvent(name: "doubleTapped", value: btnNum, isStateChange: true)
                        break
                    case "02": // HOLD
                        if (txtEnable) log.info "${device.displayName}: Button ${btnNum} held"
                        sendEvent(name: "held", value: btnNum, isStateChange: true)
                        break
                    case "04": // RELEASE
                        if (txtEnable) log.info "${device.displayName}: Button ${btnNum} released"
                        sendEvent(name: "released", value: btnNum, isStateChange: true)
                        break
                }
            }
        }
        return null 
    }

    // 2. BATTERY LOGIC (Cluster 0001)
    if (descMap.clusterId == "0001" || descMap.clusterInt == 1) {
        if (descMap.attrId == "0020" || descMap.attrInt == 32) {
            if (descMap.value != null) {
                def v = Integer.parseInt(descMap.value, 16)
                if (logEnable) log.debug "Battery Voltage: ${v/10}V"
            }
        } else if (descMap.attrId == "0021" || descMap.attrInt == 33) {
            if (descMap.value != null) {
                def rawValue = Integer.parseInt(descMap.value, 16)
                handleBattery(rawValue)
            }
        }
        return null
    }
}

def handleBattery(rawValue) {
    def pct = 0
    if (rawValue > 200) pct = 100
    else if (rawValue >= 134) pct = 100 
    else pct = (rawValue / 1.34).toInteger() 
    
    if (txtEnable) log.info "${device.displayName} Battery: ${pct}%"
    sendEvent(name: "battery", value: pct, unit: "%")
}

def refresh() {
    log.info "Refreshing battery status..."
    return zigbee.readAttribute(0x0001, [0x0020, 0x0021], [:], delay = 50)
}

def initialize() {
    int btnCount = (deviceModel ?: "4") as Integer
    sendEvent(name: "numberOfButtons", value: btnCount)
}

def configure() {
    log.info "Configuring reporting and reading attributes..."
    initialize()
    List<String> configCmds = zigbee.configureReporting(0x0001, 0x0020, 0x20, 3600, 43200, 0x01) +
                               zigbee.configureReporting(0x0001, 0x0021, 0x20, 3600, 43200, 0x01)
    return configCmds + refresh()
}
3 Likes