How to send a Z-Wave Command to a Device

I'm struggling to get the below (connectCheck) method able to send a command to my device. Specificaly basicGet() The method is triggered by a schedule.
I would then in the parse code update some part of the data like last time active or perhaps toggle a state variable. Simply to make a daily activity that can be used by one of he apps that watches code for missing reports. Mine is set for daily;

I'm hoping there is some conceptual relationship I am completely missing because right now I an garbing things from the air.

My Current code:

There are 3 different methods to send the command guess for a command report two are disabled but each has been run and provides similar errors in the logs. I keep on feeling there must be an addational action I need to make this function work. Maybe somewhere in the metada.

/**
  *  2020-02-16  v00 WaterCop Z-Wave Driver
  *				v03 removed Health Check and checkInterval as they are not supported by Hubitat.
  *				v04c cleaning up code and changing On Off to Open Closed
  *				v04d (open) Change Lucy to Configuration
  *  2021-03-16  v04f tried multple configurations for checkConnect finally found the correct option for our code.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  *  in compliance with the License. You may obtain a copy of the License at:
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  *  for the specific language governing permissions and limitations under the License.
  *
  */
 // ______________________________________________________________________________________
 metadata {
 	definition (name: "WaterCopValveV4f", namespace: "hubitat", author: "various") {
 		capability "Configuration"
 		capability "Refresh"
         capability "Actuator"
 		capability "Sensor"
 		capability "Valve"
 
 		attribute "lastCheckin", "String"
 
 		fingerprint deviceId: "0xAA08", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70"
 		fingerprint mfr:"0132", deviceJoinName: "WaterCop Valve"
 	}
 }  //
 // ______________________________________________________________________________________
 
 
 def parse(String description) {
 	log.trace description
 	def cmd = zwave.parse(description)
     log.trace "cmd=$description"
 	if (cmd) {
     log.trace zwaveEvent(cmd) 
         return zwaveEvent(cmd)
         
 	}
     state.lastActivity = new Date().time
 	return null
 }
 
 	def configure() {
 	log.debug "Configuration"
     updated()
     // zwave.basicV1.basicGet().format()
     // zwave.switchBinaryV1.switchBinaryGet().format()
 }
 
 
 def installed(){
 	response(refresh())
 }
 
 def updated(){
 		schedule("0 0 6 * * ?", connectCheck)	// once every day at 6 AM
         log.debug " connectCheck schedule 0 0 6 * * ? initiated"
 }
 
 def initialize(){
 	updated()
 }
 
 
 def refresh() {
 	log.debug "  refresh performed"
     connectCheck()
     //return zwave.switchBinaryV1.switchBinaryGet().format()
     //zwave.basicV1.basicGet().format()
 }
 
 //_________________________________________________________________________________________________
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 
 
 void connectCheck(){
 	List<hubitat.zwave.Command> cmds=[]
 	cmds.add(zwave.switchBinaryV1.switchBinaryGet())
 	cmds.add(zwave.basicV1.basicGet())
 	sendToDevice(cmds)
 }
 
 def yyyconnectCheck(){
 	// updates lastactivity so App "Device Activity Check App" will know its still communicating.
     //return zwave.basicV1.basicGet().format()   //should code as 2002
     return hubitat.zwave.commands.basicv1.BasicGet()  //.format
                                                // response should be from 2003
 }
 
 void xxxxconnectCheck(){
 	// updates lastactivity so App "Device Activity Check App" will know its still communicating.
     sendToDevice(zwave.basicV1.basicGet()) //.format()   //should code as 2002
                                                // response should be from 2003
 }
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 
 def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
 	def value = cmd.value ? "closed" : "open"
 	def event = createEvent([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])
 	return event
 }
 
 def zwaveEvent(hubitat.zwave.Command cmd) {
 	return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
 }
 
 def open() {
 	delayBetween([
 		zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format(),
 		zwave.switchBinaryV1.switchBinaryGet().format()
 	], 500)
 }
 
 def close() {
 	delayBetween([
 		zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format(),
 		zwave.switchBinaryV1.switchBinaryGet().format()
 	], 500)
 }
 
 // --- eof ---
 /**
  *  2020-02-16  v00 WaterCop Z-Wave Driver
  *				v03 removed Health Check and checkInterval as they are not supported by Hubitat.
  *				v04c cleaning up code and changing On Off to Open Closed
  *				v04d (open) Change Lucy to Configuration
  *  2021-03-16  v04f tried multple configurations for checkConnect finally found the correct option for our code.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  *  in compliance with the License. You may obtain a copy of the License at:
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  *  for the specific language governing permissions and limitations under the License.
  *
  */
 // ______________________________________________________________________________________
 metadata {
 	definition (name: "WaterCopValveV4f", namespace: "hubitat", author: "various") {
 		capability "Configuration"
 		capability "Refresh"
         capability "Actuator"
 		capability "Sensor"
 		capability "Valve"
 
 		attribute "lastCheckin", "String"
 
 		fingerprint deviceId: "0xAA08", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70"
 		fingerprint mfr:"0132", deviceJoinName: "WaterCop Valve"
 	}
 }  //
 // ______________________________________________________________________________________
 
 
 def parse(String description) {
 	log.trace description
 	def cmd = zwave.parse(description)
     log.trace "cmd=$description"
 	if (cmd) {
     log.trace zwaveEvent(cmd) 
         return zwaveEvent(cmd)
         
 	}
     state.lastActivity = new Date().time
 	return null
 }
 
 	def configure() {
 	log.debug "Configuration"
     updated()
     // zwave.basicV1.basicGet().format()
     // zwave.switchBinaryV1.switchBinaryGet().format()
 }
 
 
 def installed(){
 	response(refresh())
 }
 
 def updated(){
 		schedule("0 0 6 * * ?", connectCheck)	// once every day at 6 AM
         log.debug " connectCheck schedule 0 0 6 * * ? initiated"
 }
 
 def initialize(){
 	updated()
 }
 
 
 def refresh() {
 	log.debug "  refresh performed"
     connectCheck()
     //return zwave.switchBinaryV1.switchBinaryGet().format()
     //zwave.basicV1.basicGet().format()
 }
 
 //_________________________________________________________________________________________________
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 
 
 void connectCheck(){
 	List<hubitat.zwave.Command> cmds=[]
 	cmds.add(zwave.switchBinaryV1.switchBinaryGet())
 	cmds.add(zwave.basicV1.basicGet())
 	sendToDevice(cmds)
 }
 
 def yyyconnectCheck(){
 	// updates lastactivity so App "Device Activity Check App" will know its still communicating.
     //return zwave.basicV1.basicGet().format()   //should code as 2002
     return hubitat.zwave.commands.basicv1.BasicGet()  //.format
                                                // response should be from 2003
 }
 
 void xxxxconnectCheck(){
 	// updates lastactivity so App "Device Activity Check App" will know its still communicating.
     sendToDevice(zwave.basicV1.basicGet()) //.format()   //should code as 2002
                                                // response should be from 2003
 }
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 //________________________________________________________________________________________________
 
 def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
 	def value = cmd.value ? "closed" : "open"
 	def event = createEvent([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])
 	return event
 }
 
 def zwaveEvent(hubitat.zwave.Command cmd) {
 	return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
 }
 
 def open() {
 	delayBetween([
 		zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format(),
 		zwave.switchBinaryV1.switchBinaryGet().format()
 	], 500)
 }
 
 def close() {
 	delayBetween([
 		zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format(),
 		zwave.switchBinaryV1.switchBinaryGet().format()
 	], 500)
 }
 
 // --- eof ---

Looks like some of my methods, but I don't see them in your code..

What you will need for that to function is something like this:

void sendToDevice(List<String> cmds, Long delay=300) {
    sendHubCommand(new hubitat.device.HubMultiAction(commands(cmds, delay), hubitat.device.Protocol.ZWAVE))
}

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

void sendToDevice(String cmd, Long delay=300) {
    sendHubCommand(new hubitat.device.HubAction(zwaveSecureEncap(cmd), hubitat.device.Protocol.ZWAVE))
}

void sendToDevice(hubitat.zwave.Command cmd, Long delay=300) {
    sendHubCommand(new hubitat.device.HubAction(zwaveSecureEncap(cmd.format()), hubitat.device.Protocol.ZWAVE))
}

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

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

To add to the above, this post I wrote a while back may help explain when/how actually sending a command happens (the above is pretty much a way to "do it manually" as I alluded to in that post but didn't really explain). In your case, you have the refresh() method that isn't returning anything (its last line is connectCheck(), which Groovy would otherwise interpret as a return value, but that method is void; you could change it to def or something like List<String> and return the formatted list of commands instead, but the above should work just as well).

3 Likes

Thank you and brian so much...... I'm still absorbing.
In the meant time I found a statement in your linked post that hit an issue I was wondering about:

  • that method is the implementation of (i.e., matches the name of) a driver command (so "command" in a different sense here, things like the refresh() method for capability "Refresh" or any custom command you declare)

In the above you mention ".... abt custom command you declare...." I understand how to add custom commands where the device page gets a "button" or "tile" titled with the custom command name.

Is there a way to declare a custom command without the associated tile?

In my (poorly) written code I was using the "Refresh" tile to trigger the problem method for ease of testing. Ultimately it will be triggered by a cron schedule.
Did I shoot myself in the foot by trying to call the not working routine from Refresh?

And also from your link...

• either a single formatted Z-Wave command or a list of formatted Z-Wave commands (so technically either a String or a List

[ I understand formatted commands i.e. 2002 for basic get]

though the def you have will also work if this is what you end up with) is returned from a method.

[ I don't understand how using a def method will somehow substitute for converting the message by formatting]

It would help if you have enough time to write two methods for my understanding.

One would be BasicV1.basicGet

The 2nd would be two commands:
BasicV1.basicGet()
binarySwitchV1.binarySwitch.get()

I don't mean to ask you to write my "solution" but I'm missing a basic understanding that has hampered me for some time. Each little app or driver is painful for me to write. I was hoping I would get better but that basic piece is still kicking my ■■■.
Thanks
John

Brian thank you,

The reason you couldn't find the require methods was that I thought "SendToDevice" was a OS method.

I'm still absorbing your and Roberts kink comments.

John

1 Like

Anytime

No; all device commands (stock or custom) are shown on the device page. However, you can internally create any method you want and not expose it as a command if so desired; the tradeoff is that it's not usable for other apps, so it's only accessible to parent apps or (I think?) parent devices if yours is a child device, and vice cersa. I suppose Hubitat's stance on this is twofold: that the admin UI is, generally speaking, not intended for users to interact with devices on a daily basis (Dashboard or an app/automation would be the preferred method, then this doesn't really matter); and that if something isn't intended to be called outside of the driver itself, it probably doesn't need to be a command in the first place.

I don't see why this would be the case; assuming your goal is just to call this method on a recurring basis with something like schedule(), you can use any (non-private, but in Hubitat there probably isn't much of a need to mess with access modifiers anyway) method in your driver, even if it's not a command, for things like this, callbacks, or handlers. It can be a command (standard or custom) if you want, and this might be a logical choice for you, but it doesn't need to be.

Thank you again. I was able to get my method working. If I posted it you would think I'm silly for having an issue :slight_smile:

but I'm learning...

One question, in your posted methods (above) is your variable "cmd" always the formatted command? i.e. 2002 8601 etc
I understand "cmd" is a variable name and could be anything in some other code. But what I'm asking is in your usage is my assumption (2002 etc) correct?

Thanks
John

Bryan can undoubtedly answer in more detail about other things (and this...) than me, but in the meantime I can tell you this if it's helpful: the zwaveSecureEncap() method accepts either an actual Command object or a "formatted" command (e.g., "2002"--do note that some of these look like integers but are really hex strings) and returns a String regardless of the input. (If you know your device doesn't support security--S2 or S0--or you're only writing it for yourself and don't care, you could leave this out, but it doesn't hurt either way and is just as easy do...especially considering that if you're dealing with a Command object, you can optionally spare yourself from needing to call format() on it yourself--again, zwaveSecureEncap() returns a String either way.)

So, if I'm interpreting your question correctly, I think the answer is "it doesn't matter." :slight_smile: Otherwise, maybe something in there is helpful regardless.

1 Like

The methods I posted will take formatted or not formatted and will convert appropriately pack it with the appropriate security level for the device and send it out

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.