Using SynchronousQueues to handle Z-Wave Get / Report messages

Does anybody find it difficult to work with Z-Wave "Get" messages and wish that the get immediately returned the response report, rather than having to handle it in an event handler?

If so, here's an idea (and code sample) you might find useful. This is based on the newly added SynchronousQueues class in Hubitat 2.2.5. I use these to transfer data back from the reporting handler to the original method that initiated the get. That way, you can treat the retrieving of data much more like synchronous programming. Here's the key sample code . . .

At the top of your driver, put

import java.util.concurrent.*

Then I have a simple utility function 'myReportQueue(String reportclass)' to manage the SynchronousQueues. To avoid concurrency issues, a unique queue is created for each report class and for each instance of the driver. Thus, if I want to transfer a Configuration Report, I pass the function report class "7006" as the parameter:

// reportQueues stores a map of SynchronousQueues. When requesting a report from a device, the report handler communicates the report back to the requesting function using a queue. This makes programming more like "synchronous" programming, rather than asynchronous handling.
// This is a map within a map. The first level map is by deviceNetworkId. Since @Field static variables are shared among all devices using the same driver code, this ensures that you get a unique second-level map for a particular device. The second map is keyed by the report class hex string. For example, if you want to wait for the configurationGet report, wait for "7006".

@Field static reportQueues = new ConcurrentHashMap<String, ConcurrentHashMap>()

SynchronousQueue myReportQueue(String reportClass)
{
	ConcurrentHashMap thisDeviceQueues = reportQueues.get(device.deviceNetworkId, new ConcurrentHashMap<String,SynchronousQueue>())
	
	// Get the queue if it exists, create (new) it if it does not.
	SynchronousQueue thisReportQueue = thisDeviceQueues.get(reportClass, new SynchronousQueue())
	return thisReportQueue
}

Within a requesting function, I then send a "Get" and can wait for the response the same myReportQueues() function is used again, but this time, with the poll method to get the report. For example, notice in the following code that if I want a configuration value, I send the get, but then in the next line can get the response from the SynchronousQueue. Thus, the next line after I send the request, I can start handling the report.

void dummyFunction( ){
    
		List<hubitat.zwave.Command> cmds = []
	    cmds << secure(supervise(zwave.configurationV1.configurationSet(scaledConfigurationValue: 5, parameterNumber: 10, size: 1)))
		sendToDevice(cmds)
		
		// Wait for the report that is returned after the configurationGet, and then update the input controls so they display the updated value.
		hubitat.zwave.Command report = myReportQueue("7006").poll(10, TimeUnit.SECONDS)

                // Do whatever you want with the report that was just returned from the device.

		if (report)
		{
               // Process the returned report. It will have the full structure of the original report received at the event handler
			
		} else {
                          // Error recovery
                }
    }
}

The report handler itself is then very simple. Its really just one line to transfer back the report (though I added some logging below):


void zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport  cmd)
{ 
	Boolean transferredReport = myReportQueue(cmd.CMD).offer(cmd)
        // Add logging here if you want. You can use if (transferredReport) to test if the transfer succeeded.
}

This can be easily extended to handle endpoints. For example, in the event handler, you can use

myReportQueue(cmd.CMD).offer([command:cmd, endPoint: ep]) 

and then in the poll, "report" is a Map and you can then access report.ep and report.command

1 Like

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