Help Converting Preference to Command in Driver for Acces in Rule Machine Custom Actions

Can anyone assist me in modifying the custom driver I am using for the HomeSeer HS-FLS100+ Floodlight Sensor to convert the Lux Threshold Settings in the Preferences section of the device edit page to instead be a Command?

I pasted the existing Driver and elements I think might need to be modified below as a start, but don't know much about how to configure the Z-wave Commands in the driver to actually change the setting in the device and read the resulting setting back from the device.

Any help would be much appreciated!!!!

Let me explain what I am trying to do:

The reasoning is that this Preference controls the Level of the Lux reading below which a motion detection from the PIR sensor causes the device to send a Light On command for the device. The normal use case for me is to have this set at 30 Lux, and then when it's fairly dark, the light comes on with motion, and ceases to come on with motion when it is greater than 30 Lux.

This all works great to create a regular night only motion light, but there are occasions when I don't want the lights to come on with motion at all. To stop it from coming on with motion at all, I can do one of 2 things when using the existing Driver:

  1. Set this Preference in each device to zero, one at a time, in the device edit page, which would probably be tolerable if I only had one, but there are currently 6 of them

  2. A more draconian method is to cut the power to all the devices. Since these are mains powered devices that become Z-Wave repeaters, cutting the power knocks them out of the Z-wave mesh causing routing issues, and it also means the motion and Lux sensors become unavailable, which are used in many other rules, causing other issues.

So what I want to do is have a virtual switch on the dashboard that triggers a Rule Machine rule to change this Preference in each light to zero, which tells them to not send the Light On commands with motion. However since this is a Preference and not a Command, I don't believe I can access this Preference in Rule Machine, at least I don't see it available in the Custom Actions section for the device in Rule Machine. So I believe I need to convert this Preference to a Command.

I believe I can add an enumerated list as a Command in the driver with this syntax:

command(
             "setLuxThreshold", 
             [
                [
                     "name":"Lux Threshold",
                     "description":"Set the Lux level below which Motion sends a Light On command to the device.  Range: 0,255,30-200.  0=Never Turn Light On with Motion, 255=Always Turn Light On with Motion (ignore Lux)",
                     "type":"ENUM",
                     "constraints":["0 (Never Turn On)","255 (Always Turn On)","30","40","50 (default)","60","70","80","90","100","110","120","130","140","150","160","170","190","190","200"]
                ]
             ]
        );

From the existing driver and the Manual, I see that this is Parameter 2 that is being set with a size of 2. So I think I might be able to add the Command Z-Wave actions themselves with something like this, which I saw implemented in a GE Z-Wave Plus Motion Dimmer driver that has an enumerated Command:

void setLuxThreshold(value) {
    if (logEnable) log.debug "Setting Lux Threshold value: ${value}"
    def cmds = []        
    // "0 (Never Turn On)","255 (Always Turn On)","30","40","50 (default)","60","70","80","90","100","110","120","130","140","150","160","170","190","190","200"
    switch (value) {
        case "0 (Never Turn On)":
            state.luxThreshold = "0 (Never Turn On)"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 0 , parameterNumber: 2, size: 2).format()
            break
        case "255":
            state.luxThreshold = "255 (Always Turn On)"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 255 , parameterNumber: 2, size: 2).format()
            break
        case "30":
            state.luxThreshold = "30"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 30 , parameterNumber: 2, size: 2).format()
            break
        case "40":
            state.luxThreshold = "40"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 40 , parameterNumber: 2, size: 2).format()
            break
        case "50 (default)":
            state.luxThreshold = "50 (default)"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 50 , parameterNumber: 2, size: 2).format()
            break
        case "40":
            state.luxThreshold = "60"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 60 , parameterNumber: 2, size: 2).format()
            break
        case "70":
            state.luxThreshold = "70"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 70 , parameterNumber: 2, size: 2).format()
            break
        case "80":
            state.luxThreshold = "80"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 80 , parameterNumber: 2, size: 2).format()
            break
        case "90":
            state.luxThreshold = "90"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 90 , parameterNumber: 2, size: 2).format()
            break
        case "100":
            state.luxThreshold = "100"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 100 , parameterNumber: 2, size: 2).format()
            break
        case "110":
            state.luxThreshold = "110"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 110 , parameterNumber: 2, size: 2).format()
            break
        case "120":
            state.luxThreshold = "120"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 120 , parameterNumber: 2, size: 2).format()
            break
        case "130":
            state.luxThreshold = "130"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 130 , parameterNumber: 2, size: 2).format()
            break
        case "140":
            state.luxThreshold = "140"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 140 , parameterNumber: 2, size: 2).format()
            break
        case "150":
            state.luxThreshold = "150"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 150 , parameterNumber: 2, size: 2).format()
            break
        case "160":
            state.luxThreshold = "160"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 160 , parameterNumber: 2, size: 2).format()
            break
        case "170":
            state.luxThreshold = "170"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 170 , parameterNumber: 2, size: 2).format()
            break
        case "180":
            state.luxThreshold = "180"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 180 , parameterNumber: 2, size: 2).format()
            break
        case "190":
            state.luxThreshold = "190"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 190 , parameterNumber: 2, size: 2).format()
            break
        case "200":
            state.luxThreshold = "200"
            cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: 200 , parameterNumber: 2, size: 2).format()
            break
        default:
            return
    }
  	cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format()
    delayBetween(cmds, 500)
}

Then in that GE Z-Wave Plus Motion Dimmer driver, it might be using this type of syntax to get the value from the device, but I'm not sure, and I also realize that I only really have Case 2 here, which I thinkk might relate to Parameter 2, but I really have no idea?

void zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
	if (logEnable) log.debug "---CONFIGURATION REPORT V1--- ${device.displayName} sent ${cmd}"
    def config = cmd.scaledConfigurationValue.toInteger()
    def result = []
	def name = ""
    def value = ""
    def reportValue = config // cmd.configurationValue[0]
    switch (cmd.parameterNumber) {
        case 2:
            name = "Lux Threshold"
    // "0 (Never Turn On)","255 (Always Turn On)","30","40","50 (default)","60","70","80","90","100","110","120","130","140","150","160","170","190","190","200"
            value = reportValue == 0 ? "0 (Never Turn On)" : reportValue == 255 ? "255 (Always Turn On)" : reportValue == 30 ? "30" : reportValue == 40 ? "40" : reportValue == 50 ? "50 (default)" : reportValue == 50 ? "50" : reportValue == 60 ? "60" : reportValue == 70 ? "70" : reportValue == 80 ? "80" : reportValue == 90 ? "90" : reportValue == 100 ? "100" : reportValue == 110 ? "110" : reportValue == 120 ? "120" : reportValue == 130 ? "130" : reportValue == 140 ? "140" : reportValue == 150 ? "150" : reportValue == 160 ? "160" : reportValue == 170 ? "170" : reportValue == 180 ? "180" : reportValue == 190 ? "190" : reportValue == 200 ? "200"  : "error"
            if (value == 0) {state.luxThreshold = "0 (Never Turn On)"}
            else if (value == 255) {state.luxThreshold = "255 (Always Turn On)"}
            else if (value == 30) {state.luxThreshold = "30"}
            else if (value == 40) {state.luxThreshold = "40"}
            else if (value == 50) {state.luxThreshold = "50 (default)"}
            else if (value == 60) {state.luxThreshold = "60"}
            else if (value == 70) {state.luxThreshold = "70"}
            else if (value == 80) {state.luxThreshold = "80"}
            else if (value == 90) {state.luxThreshold = "90"}
            else if (value == 100) {state.luxThreshold = "100"}
            else if (value == 110) {state.luxThreshold = "110"}
            else if (value == 120) {state.luxThreshold = "120"}
            else if (value == 130) {state.luxThreshold = "130"}
            else if (value == 140) {state.luxThreshold = "140"}
            else if (value == 150) {state.luxThreshold = "150"}
            else if (value == 160) {state.luxThreshold = "160"}
            else if (value == 170) {state.luxThreshold = "170"}
            else if (value == 180) {state.luxThreshold = "180"}
            else if (value == 190) {state.luxThreshold = "190"}
            else if (value == 200) {state.luxThreshold = "200"}
            break
    }
	result << createEvent([name: name, value: value, displayed: false])
	return result
}

Lots of questions:

  1. Are these the correct elements to adjust

  2. Is it the correct syntax

  3. Are they the correct Z-Wave commands to make the setting in the device

  4. Will this also report back the setting that has been made and accepted by the device

  5. Will this then show-up in Rule Machine as a Custom Action

  6. Do I need to delete the line in the exisitng driver to now eliminate this as a Preference

         2: [input: [name: "configParam2", type: "enum", title: "Lux Threshold Settings", description: "", defaultValue: 50, options:[0:"Always don't turn on light",255:"Turn Light on by PIR",30:"Turn light on @ 30 Lux",40:"Turn light on @ 40 lux",50:"Turn light on @ 50 lux", 60:"Turn light on @ 60 lux",70:"Turn light on @ 70 lux",80:"Turn light on @ 80 lux", 90:"Turn light on @ 90 lux", 100:"Turn light on @ 100 lux", 110:"Turn light on @ 110 lux", 120:"Turn light on @ 120 lux", 130:"Turn light on @ 130 lux", 140:"Turn light on @ 140 lux", 150:"Turn light on @ 150 lux", 160:"Turn light on @ 160 lux", 170:"Turn light on @ 170 lux", 180:"Turn light on @ 180 lux", 190:"Turn light on @ 190 lux", 200:"Turn light on @ 200 lux"]], parameterSize: 2],
    

Any help would be Greatly appreciated!!!

Full existing driver for reference:

/*
*	HomeSeer HS-FLS100+ Floodlight Sensor
*	version: 1.4
*/

import groovy.transform.Field

metadata {
    definition (name: "HomeSeer HS-FLS100+ Floodlight Sensor", namespace: "djdizzyd", author: "Bryan Copeland", importUrl: "https://raw.githubusercontent.com/djdizzyd/hubitat/master/Drivers/HomeSeer/HS-FLS100-Floodlight-Sensor.groovy") {
        capability "Switch"
        capability "Refresh"
        capability "Actuator"
        capability "Sensor"
        capability "Configuration"
        capability "MotionSensor"
        capability "IlluminanceMeasurement"

        fingerprint mfr:"000C", prod:"0201", deviceId:"000B", inClusters:"0x5E,0x85,0x59,0x55,0x86,0x72,0x5A,0x73,0x9F,0x6C,0x7A,0x71,0x25,0x31,0x70,0x30", deviceJoinName: "HomeSeer HS-FLS100+"
    }
    preferences {
        configParams.each { input it.value.input }
        input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
        input name: "txtEnable", type: "bool", title: "TXT Descriptive logging", defaultValue: false
    }
}
@Field static Map configParams = [
        1: [input: [name: "configParam1", type: "number", title: "PIR Trigger Off period", description: "seconds 8-720", defaultValue: 15, range:"8..720"], parameterSize: 2],
        2: [input: [name: "configParam2", type: "enum", title: "Lux Threshold Settings", description: "", defaultValue: 50, options:[0:"Always don't turn on light",255:"Turn Light on by PIR",30:"Turn light on @ 30 Lux",40:"Turn light on @ 40 lux",50:"Turn light on @ 50 lux", 60:"Turn light on @ 60 lux",70:"Turn light on @ 70 lux",80:"Turn light on @ 80 lux", 90:"Turn light on @ 90 lux", 100:"Turn light on @ 100 lux", 110:"Turn light on @ 110 lux", 120:"Turn light on @ 120 lux", 130:"Turn light on @ 130 lux", 140:"Turn light on @ 140 lux", 150:"Turn light on @ 150 lux", 160:"Turn light on @ 160 lux", 170:"Turn light on @ 170 lux", 180:"Turn light on @ 180 lux", 190:"Turn light on @ 190 lux", 200:"Turn light on @ 200 lux"]], parameterSize: 2],
        3: [input: [name: "configParam3", type: "number", title: "Lux level report", description: "minutes 0 to 1440 minutes", defaultValue: 10, range:"0..1440"], parameterSize: 2]
]
@Field static Map ZWAVE_NOTIFICATION_TYPES=[0:"Reserverd", 1:"Smoke", 2:"CO", 3:"CO2", 4:"Heat", 5:"Water", 6:"Access Control", 7:"Home Security", 8:"Power Management", 9:"System", 10:"Emergency", 11:"Clock", 12:"First"]
@Field static Map CMD_CLASS_VERS=[0x20:1,0x86:2,0x72:2,0x5B:3,0x70:1,0x85:2,0x59:1,0x31:5,0x71:4]

void logsOff(){
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable",[value:"false",type:"bool"])
}

void configure() {
    if (!state.initialized) initializeVars()
    runIn(5,pollDeviceData)
}

void initializeVars() {
    // first run only
    state.initialized=true
    runIn(5, refresh)
}

void updated() {
    log.info "updated..."
    log.warn "debug logging is: ${logEnable == true}"
    unschedule()
    if (logEnable) runIn(1800,logsOff)
    List<hubitat.zwave.Command> cmds=[]
    cmds.addAll(runConfigs())
    sendToDevice(cmds)
}

List<hubitat.zwave.Command> runConfigs() {
    List<hubitat.zwave.Command> cmds=[]
    configParams.each { param, data ->
        if (settings[data.input.name]) {
            cmds.addAll(configCmd(param, data.parameterSize, settings[data.input.name]))
        }
    }
    return cmds
}

List<hubitat.zwave.Command> pollConfigs() {
    List<hubitat.zwave.Command> cmds=[]
    configParams.each { param, data ->
        if (settings[data.input.name]) {
            cmds.add(zwave.configurationV1.configurationGet(parameterNumber: param.toInteger()))
        }
    }
    return cmds
}

List<hubitat.zwave.Command> configCmd(parameterNumber, size, scaledConfigurationValue) {
    List<hubitat.zwave.Command> cmds = []
    cmds.add(zwave.configurationV1.configurationSet(parameterNumber: parameterNumber.toInteger(), size: size.toInteger(), scaledConfigurationValue: scaledConfigurationValue.toInteger()))
    cmds.add(zwave.configurationV1.configurationGet(parameterNumber: parameterNumber.toInteger()))
    return cmds
}

void zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
    if(configParams[cmd.parameterNumber.toInteger()]) {
        Map configParam=configParams[cmd.parameterNumber.toInteger()]
        int scaledValue
        cmd.configurationValue.reverse().eachWithIndex { v, index -> scaledValue=scaledValue | v << (8*index) }
        device.updateSetting(configParam.input.name, [value: "${scaledValue}", type: configParam.input.type])
    }
}

void pollDeviceData() {
    List<hubitat.zwave.Command> cmds = []
    cmds.add(zwave.versionV2.versionGet())
    cmds.add(zwave.manufacturerSpecificV2.deviceSpecificGet(deviceIdType: 1))
    cmds.addAll(processAssociations())
    cmds.addAll(pollConfigs())
    cmds.add(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: 1, sensorType: 3))
    cmds.add(zwave.notificationV4.notificationGet(notificationType: 7, event:0))
    cmds.add(zwave.switchBinaryV1.switchBinaryGet())
    sendToDevice(cmds)
}

void refresh() {
    List<hubitat.zwave.Command> cmds=[]
    cmds.add(zwave.switchBinaryV1.switchBinaryGet())
    cmds.add(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: 1, sensorType: 3))
    cmds.add(zwave.notificationV4.notificationGet(notificationType: 7, event:0))
    sendToDevice(cmds)
}

void installed() {
    if (logEnable) log.debug "installed()..."
    initializeVars()
}

void eventProcess(Map evt) {
    if (device.currentValue(evt.name).toString() != evt.value.toString() || !eventFilter) {
        evt.isStateChange=true
        sendEvent(evt)
    }
}

void zwaveEvent(hubitat.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
    if (logEnable) log.debug "${cmd}"
    Map evt = [isStateChange:false]
    switch (cmd.sensorType) {
        case 3:
            evt.name = "illuminance"
            evt.value = cmd.scaledSensorValue.toInteger()
            evt.unit = "lux"
            evt.isStateChange=true
            evt.descriptionText="${device.displayName}: Illuminance report received: ${evt.value}"
            break
    }
    if (evt.isStateChange) {
        if (txtEnable) log.info evt.descriptionText
        eventProcess(evt)
    }
}

void zwaveEvent(hubitat.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) {
    if (logEnable) log.debug "Sensor binary report: ${cmd}"
    // redundant and un-needed function
}

void zwaveEvent(hubitat.zwave.commands.notificationv4.NotificationReport cmd) {
    Map evt = [isStateChange:false]
    if (logEnable) log.info "Notification: " + ZWAVE_NOTIFICATION_TYPES[cmd.notificationType.toInteger()]
    if (cmd.notificationType==7) {
        // home security
        switch (cmd.event) {
            case 0:
                // state idle
                if (cmd.eventParametersLength > 0) {
                    switch (cmd.eventParameter[0]) {
                        case 7:
                            evt.name = "motion"
                            evt.value = "inactive"
                            evt.isStateChange = true
                            evt.descriptionText = "${device.displayName} motion became ${evt.value}"
                            break
                        case 8:
                            evt.name = "motion"
                            evt.value = "inactive"
                            evt.isStateChange = true
                            evt.descriptionText = "${device.displayName} motion became ${evt.value}"
                            break
                    }
                } else {
                    evt.name = "motion"
                    evt.value = "inactive"
                    evt.descriptionText = "${device.displayName} motion became ${evt.value}"
                    evt.isStateChange = true
                }
                break
            case 7:
                // motion detected (location provided)
                evt.name = "motion"
                evt.value = "active"
                evt.isStateChange = true
                evt.descriptionText = "${device.displayName} motion became ${evt.value}"
                break
            case 8:
                // motion detected
                evt.name = "motion"
                evt.value = "active"
                evt.isStateChange = true
                evt.descriptionText = "${device.displayName} motion became ${evt.value}"
                break
            case 254:
                // unknown event/state
                log.warn "Device sent unknown event / state notification"
                break
        }
    }
    if (evt.isStateChange) {
        if (txtEnable) log.info evt.descriptionText
        eventProcess(evt)
    }
}

void zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd) {
    if (logEnable) log.debug cmd
    switchEvents(cmd)
}

private void switchEvents(hubitat.zwave.Command cmd) {
    String value = (cmd.value ? "on" : "off")
    String description = "${device.displayName} was turned ${value}"
    if (txtEnable) log.info description
    eventProcess(name: "switch", value: value, descriptionText: description, type: state.isDigital?"digital":"physical")
    state.isDigital=false
}

void zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
    if (logEnable) log.debug cmd
    switchEvents(cmd)
}

void on() {
    state.isDigital=true
    sendToDevice(zwave.basicV1.basicSet(value: 0xFF))
}

void off() {
    state.isDigital=true
    sendToDevice(zwave.basicV1.basicSet(value: 0x00))
}

void zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
    hubitat.zwave.Command encapsulatedCommand = cmd.encapsulatedCommand(CMD_CLASS_VERS)
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand)
    }
}

void parse(String description) {
    if (logEnable) log.debug "parse:${description}"
    hubitat.zwave.Command cmd = zwave.parse(description, CMD_CLASS_VERS)
    if (cmd) {
        zwaveEvent(cmd)
    }
}

void zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd) {
    if (logEnable) log.debug "Supervision get: ${cmd}"
    hubitat.zwave.Command encapsulatedCommand = cmd.encapsulatedCommand(CMD_CLASS_VERS)
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand)
    }
    sendToDevice(new hubitat.zwave.commands.supervisionv1.SupervisionReport(sessionID: cmd.sessionID, reserved: 0, moreStatusUpdates: false, status: 0xFF, duration: 0))
}

void zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
    if (logEnable) log.debug "Device Specific Report: ${cmd}"
    switch (cmd.deviceIdType) {
        case 1:
            // serial number
            def serialNumber=""
            if (cmd.deviceIdDataFormat==1) {
                cmd.deviceIdData.each { serialNumber += hubitat.helper.HexUtils.integerToHexString(it & 0xff,1).padLeft(2, '0')}
            } else {
                cmd.deviceIdData.each { serialNumber += (char) it }
            }
            device.updateDataValue("serialNumber", serialNumber)
            break
    }
}

void zwaveEvent(hubitat.zwave.commands.versionv2.VersionReport cmd) {
    if (logEnable) log.debug "version2 report: ${cmd}"
    device.updateDataValue("firmwareVersion", "${cmd.firmware0Version}.${cmd.firmware0SubVersion}")
    device.updateDataValue("protocolVersion", "${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}")
    device.updateDataValue("hardwareVersion", "${cmd.hardwareVersion}")
}

void sendToDevice(List<hubitat.zwave.Command> cmds) {
    sendHubCommand(new hubitat.device.HubMultiAction(commands(cmds), hubitat.device.Protocol.ZWAVE))
}

void sendToDevice(hubitat.zwave.Command cmd) {
    sendHubCommand(new hubitat.device.HubAction(secureCommand(cmd), hubitat.device.Protocol.ZWAVE))
}

void sendToDevice(String cmd) {
    sendHubCommand(new hubitat.device.HubAction(secureCommand(cmd), hubitat.device.Protocol.ZWAVE))
}

List<String> commands(List<hubitat.zwave.Command> cmds, Long delay=300) {
    return delayBetween(cmds.collect{ secureCommand(it) }, delay)
}

String secureCommand(hubitat.zwave.Command cmd) {
    secureCommand(cmd.format())
}

String secureCommand(String cmd) {
    String encap=""
    if (getDataValue("zwaveSecurePairingComplete") != "true") {
        return cmd
    } else {
        encap = "988100"
    }
    return "${encap}${cmd}"
}

void zwaveEvent(hubitat.zwave.Command cmd) {
    if (logEnable) log.debug "skip:${cmd}"
}

List<hubitat.zwave.Command> setDefaultAssociation() {
    List<hubitat.zwave.Command> cmds=[]
    cmds.add(zwave.associationV2.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId))
    cmds.add(zwave.associationV2.associationGet(groupingIdentifier: 1))
    return cmds
}

List<hubitat.zwave.Command> processAssociations(){
    List<hubitat.zwave.Command> cmds = []
    cmds.addAll(setDefaultAssociation())
    if (logEnable) log.debug "processAssociations cmds: ${cmds}"
    return cmds
}


void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
    if (logEnable) log.debug "${device.label?device.label:device.name}: ${cmd}"
    List<String> temp = []
    if (cmd.nodeId != []) {
        cmd.nodeId.each {
            temp.add(it.toString().format( '%02x', it.toInteger() ).toUpperCase())
        }
    }
    updateDataValue("zwaveAssociationG${cmd.groupingIdentifier}", "$temp")
}

void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationGroupingsReport cmd) {
    if (logEnable) log.debug "${device.label?device.label:device.name}: ${cmd}"
    log.info "${device.label?device.label:device.name}: Supported association groups: ${cmd.supportedGroupings}"
    state.associationGroups = cmd.supportedGroupings
}

For reference here is the device edit screen with the exiting driver:

How easy do you want to make this? :slight_smile: If you don't care about the value of the preference in the UI dynamically updating (just keep in mind that if you create both the command and the preference, the preference will overwrite the parameter value--which could have last been set via the command--if you hit "Save Preferences" in the driver), then you could take the easy/lazy way out. Just add something like

command "setLuxThreshold", ["NUMBER"]

to the definition in your driver, then implement a method for the command that could look something like:

def setLuxThreshold(Integer value) {
   if (logEnable) log.debug "setLuxThreshold(${value})"
   return zwaveSecureEncap(zwave.configurationV1.configurationSet(scaledConfigurationValue: value, parameterNumber: 2, size: 2).format())
}

You can definitely make this fancier if you want; you can make the UI provide a description and options for the command, but if you're just writing this for yourself and understand the significance of 0, 255, and any value in between, you don't technically need to.

If you want, as suggested above, the UI to update when the parameter changes, then you might need to send a configurationGet() after setting the value. You'd definitely also need to respond to that report and perhaps update the value of the preference using the device.updateSetting("mySettingName", [value: myNewValue, type: "number"]) method. You don't need to store the parameter value in state; some driver authors do because on a "Save Preferences" or "Configure," they'll only send ones that changed, and for sleepy devices, it may be useful for tracking what still needs to be sent when the device wakes up next.

It looks like you've tried most the above, but I didn't look too closely at exactly what everything does. It's far fancier than I'd write for my own use, where I'd probably just add the command/method and call it good. :slight_smile: But if you want help with more, I could look more closely!

Thanks a lot for the descriptive answer. I'm completely new to looking at how the driver works, and didn't/don't understand the purpose of each piece of code, and the z-wave syntax and commands themselves are also a mystery. So decoding exactly what to do hasn't been easy.

I'll first try your simple fix suggestion all by itself and report back if that just works, and then I might try to get a little more descriptive for my own learning, but getting it to work is job 1 right now. Do you think this will expose the setLuxThreshold as a Custom Action in Rule Machine, which is the ultimate goal?

Is it ok to then to just delete the parameter 2 line in the Preference section of the original driver, or will this mess something up? I don't want to have the situation you describe arise where I accidentally override everything by clicking "Save Preferences".

I would like the Driver to report back what the setting is, so I can understand if the command was accepted. So it seems it will need your suggested configurationGet() and device.updateSetting("mySettingName", [value: myNewValue, type: "number"]) methods.

The original driver seems to already implement configurationGet() inside 2 List functions. Will this already do what you think is needed? From what I can tell, it looks like those List functions are called by a number of other driver code sections. Are all those List calls what keeps the page updated upon each of those actions?

The original driver also implements the device.updateSetting("mySettingName", [value: myNewValue, type: "number"]) method inside of one of the zwaveEvent functions. Specifically the zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) function. However, there appears to be many zwaveEvent functions, and that is where it gets very confusing for me. Do you think the device.updateSetting("mySettingName", [value: myNewValue, type: "number"]) is in a part of the code that will do what you think is needed, or do I need to modify it.

Again, much appreciate the help. It's a great learning for me, and I think will also help out anyone else who might be trying to do something similar. I'll post back in here whatever finally actually works.

Your suggested quick fix worked!!

I did add a few descriptions etc. to the Command button, but editing the device driver as indicated below allows me to setLuxThreshold value in the device edit screen via a Command instead of a Preference. Which then allows me to achieve exactly what I was trying to make happen in the grand scheme. The setLuxThreshold Command now also shows up in Rule Machine as a Custom Action, and I was able to trigger a Rule with a virtual switch from a Dashboard that then sets the setLuxThreshold value from the Rule for the device based on the state of the virtual switch. For example, Virtual Switch On = setLuxThreshold(255), and Virtual Switch Off = setLuxThreshold(0). The device takes the Command set by Rule Machine, and behaves exactly as expected for the given setting. Works perfectly!!!!!

Changes made to the Driver Code:

In the metadata { } section, added the Command Button:

command "setLuxThreshold", [[name:"Lux Threshold",type:"NUMBER", description:"Set the Lux level below which Motion sends a Light On command to the device. Range: 0,255,30-200. 0=Never Turn Light On with Motion, 255=Always Turn Light On with Motion (ignore Lux)"]]

In the @Field static Map configParams = [ ] section, Commented out the original Parameter 2 Preference:

// 2: [input: [name: "configParam2", type: "enum", title: "Lux Threshold Settings", description: "", defaultValue: 50, options:[0:"Always don't turn on light",255:"Turn Light on by PIR",30:"Turn light on @ 30 Lux",40:"Turn light on @ 40 lux",50:"Turn light on @ 50 lux", 60:"Turn light on @ 60 lux",70:"Turn light on @ 70 lux",80:"Turn light on @ 80 lux", 90:"Turn light on @ 90 lux", 100:"Turn light on @ 100 lux", 110:"Turn light on @ 110 lux", 120:"Turn light on @ 120 lux", 130:"Turn light on @ 130 lux", 140:"Turn light on @ 140 lux", 150:"Turn light on @ 150 lux", 160:"Turn light on @ 160 lux", 170:"Turn light on @ 170 lux", 180:"Turn light on @ 180 lux", 190:"Turn light on @ 190 lux", 200:"Turn light on @ 200 lux"]], parameterSize: 2],

In the portion of the Driver below the void off() { } function, added the following:

def setLuxThreshold(value) {
    if (logEnable) log.debug "Setting Lux Threshold value: ${value}"
    state.luxThreshold = "${value}"
    return zwaveSecureEncap(zwave.configurationV1.configurationSet(scaledConfigurationValue: value, parameterNumber: 2, size: 2).format())
}

I did add the State variable in there as right now this change to the driver code doesn't report back what the device setting is for Parameter 2, and at least the state.luxThreshold variable reports out on the device edit screen what was sent to the device. However, the State variable only refreshes when the actual webpage is reloaded.

So now to figure out how to get the configurationGet() and device.updateSetting("mySettingName", [value: myNewValue, type: "number"]) methods to work...

If you want the driver to report back the value by setting the preference to your desired value, then you'll need to un-un-comment the line that got rid of the preference (and then update it with updateSetting() as new values come in from the ConfigurationReport for this parameter). But you only need the preference if you want to see and set the value from the device page without using the command. If you only care about the command, writing the value to state and just looking at the state values of the device on that page isn't a bad idea, either.

Either way would require a reload of the page to update--nothing dynamically updates except "Current States," and I wouldn't bother with putting anything there that you don't want to respond to (say, from an app) as an event, which is the purpose that those attributes/values serve.

Thanks for the additional info. I am going to keep this without the Preference field, so won't then need the updateSetting(). I also can't see a reason to trigger anything off an event by changing this settings, and will therefore also not mess with the "Current States" area either.

So with that, I am going to say this is done as it is working great!!

The help was much appreciated!!!

1 Like