[BUG?] refresh() by runEveryXXMinutes() ignores commands that are executed by a manual refresh()

Hi There,

Making @nayelyz and @jody.albritton aware of a weird behavior I’ve seen that I can only think it’s a bug,

when running a refresh() that has a delayBetween(), with multiple zigbee.readAttribute() commands, this happens:

  • if the refresh() is called by runEveryXXMinutes(), it will only execute one zigbee.readAttribute() command out of 4 (always the same one)

  • if the refresh() is called manually by dragging from top to bottom the device page for a manual refresh, it will get 3* out of 4 of the commands.

  • I’m almost sure that using zigbee.onOffRefresh() means you will never see the on/off zigbee response using the parse() method.

Screenshot:

DTH Code: https://raw.githubusercontent.com/cristianhares/smartthings/main/devicetypes/chares/xiaomi-zigbee-smart-outlet/xiaomi-zigbee-smart-outlet.groovy

It sounds to me like a bug on the delayBetween when its inside a refresh, called by different functions (an actual manual refresh, vs a scheduled delay), or am I doing something wrong :woozy_face: ?

Can you take a look at it? Thanks!

Are you relying on the debut console output to determine if the commands are executing? If so that is not reliable. The debut console won’t print every debug messages if the server is overloaded or there are too many messages to print in the buffer.

Partially, but I can easily see the behavior happening in the App, and the events that are created on the device,

So I know this is happening and want to know if its a bug of the platform (like why does it executes 2 refresh() commands sometimes), or is an undocumented incompatibility of the java groovy methods used.

I’ve seen the debut console print the same log message twice when the servers are out of sync due to timing issues I suspect. You could be running into that. The only way to know for sure is to keep a running counter and print it.

Yup, I know the action itself happens twice, and its not just a timing issue unfortunately :frowning:

For the moment I don’t fully mind that an actual action runs twice like the refresh(), I’m more concerned on why a refresh() command would run differently when scheduled vs manual.

Hi, there! How are you monitoring the events created on the device?
The commands could be sent but the response or logs might be dropped and not shown.

Heya @nayelyz :slight_smile:

I know the debug ain’t reliable, but I did create an attribute and a counter that adds up to that attribute.

I can verify that a manual refresh() command sends the report attribute commands correctly, but a scheduled runEveryXXMinutes() doesn’t do them all (and curiously enough its always the 3rd command the one that is sent, and im not sure about the onOffRefresh().

Edit: oh and also I can literally see the refresh happen in the App GUI on a manual, but never on a schedule. (and that no values get registered behind the scenes, so the vertical bars graph doesnt get data to see what changed).

I discussed this with our engineering team. The feedback is:

  • To send the commands on a schedule, you need to send them explicitly.
  • delayBetween doesn’t execute commands itself.
  • When refresh is executed manually, a list of Hub actions is expected. If you just call the function from another, that list is ignored.

The suggested method is wrapping this list with sendHubCommand for the schedule, for example:

def getRefreshCommands() {
 [...zigbee.readAttribute()...]
}
def refresh() {
 delayBetween(refreshCommands, 500)
}
def scheduledRefresh() {
 sendHubCommand(refreshCommands, 500)
}
...
runEvery10Minutes(scheduledRefresh)
...
1 Like

The refresh action in the UI, or the refresh command being called from another app, results in the refresh() method being called as a command. If refresh() is called by the scheduler it is just being used as an ordinary Groovy method. The difference is that if you return Zigbee commands from a command, either as an explicit return value or as the value of the last statement executed, they will get sent to the hub automagically (same with Z-Wave commands or hubActions). In an ordinary Groovy method it is down to you to send them.

So I’d expect there to be a difference, but I can’t relate it to what you are seeing as it is something I haven’t worked with.

Thanks @nayelyz! This clarifications now make more sense to me on how delayBetween actually works, and is interesting that the runEveryXXMinutes requires a separate method with the sendHubCommand instead of just calling the refresh method (as you said, send them explicitly).

I might have skipped a part that I didn’t see/read, but has this been documented? This is interesting :smiley: !

@orangebucket Thanks for the info! its a good thing that I know know there’s an actual difference on how the refresh() is called, going to document that in the DTH :slight_smile:

Thanks again to both!!

1 Like

Heya @nayelyz, now I remember why I did it the way I did originally (that’s why I didn’t mark you answer as a solution yet until I tested it)

If you call a method via runEveryXXMinutes() that is not refresh, it will not run the method.

Note: note that I did as the documentation says, that runEveryXXMinutes() must not call it with (), and the method must not be private. Yet even so, it’ll not run scheduledRefresh(). Also that sendHubCommand is not prepared to call a method as a string, must be called as sendHubCommand (method()). Same goes for the delayBetween, method must be called as method().

Here’s my example, using your comments:

metadata {
	definition (...............) {
		capability "Actuator"
		capability "Configuration"
		capability "Refresh"
		capability "Switch"
		capability "Temperature Measurement"
		capability "Sensor"
		capability "Power Meter"
		capability "Energy Meter"
		capability "Health Check"

	}

	fingerprint ..............

	def rates = [:]
	rates << ["5" : "Refresh data every 5 minutes"]
	rates << ["10" : "Refresh data every 10 minutes"]
	rates << ["15" : "Refresh data every 15 minutes"]
	rates << ["30" : "Refresh data every 30 minutes"]

	preferences {
		input name: "refreshRate", type: "enum", title: "Refresh time rate", description: "Change the data poll refresh rate (default: 10)", options: rates, required: false, defaultValue: "10"

	}
}

def installed() {
	sendEvent(name: "checkInterval", value: 720, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}

// updated() seems to happen for example after you save settings in the device (it also seems to happen after install)
def updated() {
	log.info "Updating settings of the device"

	unschedule()

	switch(refreshRate) {
		case "5":
			runEvery5Minutes(scheduledRefresh)
			log.info "Data poll refresh rate has been updated and is scheduled to run every 5 minutes"
			break
		case "15":
			runEvery15Minutes(scheduledRefresh)
			log.info "Data poll refresh rate has been updated and is scheduled to run every 15 minutes"
			break
		case "30":
			runEvery30Minutes(scheduledRefresh)
			log.info "Data poll refresh rate has been updated and is scheduled to run every 30 minutes"
			break
		default:
			runEvery10Minutes(scheduledRefresh)
			log.info "Data poll refresh rate has been updated and is scheduled to run every 10 minutes"
	}
}

def ping() {
	scheduledRefresh()
}

def configure() {
	log.info "Configuring Reporting and Bindings, setting schedule and forcing first report"

	delayBetween(zigbeeReportCommands(), 500)
}

def refresh() {
	log.info "Running refresh command and requesting refresh values from device"

	delayBetween(zigbeeRefreshCommands(), 500)
}

def zigbeeRefreshCommands() {
	[
		zigbee.onOffRefresh(),
		zigbee.readAttribute(0x0002, 0x0000),
		zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0002]),
		zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0003])
	]
}


def zigbeeReportCommands() {
	[
		zigbee.onOffConfig(),
		zigbee.configureReporting(0x0002, 0x0000, 0x29, 1, 300, 0x01),
		zigbee.configureReporting(0x000C, 0x0055, 0x39, 1, 300, 0x01, [destEndpoint: 0x0002]),
		zigbee.configureReporting(0x000C, 0x0055, 0x39, 1, 300, 0x01, [destEndpoint: 0x0003])
	]
}

def scheduledRefresh() {
	log.info "Running scheduled refresh command and requesting refresh values from device"

	sendHubCommand(zigbeeRefreshCommands(), 500)
}

This is why I did it the way I did before (and forgot why), the runEveryXXMinutes() will not execute in my case something that is not refresh().

I know the switch clause happens, as the log.info prints happen. but the scheduled tasks don’t when they are not a refresh().

Either I’m missing some method nuance like the way it has to be done with delayBetween(), or there something weird going on.

Thanks for the info, please, allow me some time to verify this with our engineering dept. I’ll keep you posted.

1 Like

Hey @nayelyz, did you get a responde from the team? :slight_smile: , Thanks!

Not really my area, but that doesn’t look right to me. To me it would make more sense as either

sendHubCommand( refresh() )

or

sendHubCommand( delayBetween(zigbeeRefreshCommands(), 500) )

Hey @orangebucket, the delay is actually a valid option on the sendHubCommand :), there’s an obscure reference to it in point 284.1.60 of the PDF version of the ‘old’ docs: https://docs.smartthings.com/_/downloads/en/latest/pdf/ (took me a while to find it again :stuck_out_tongue: )


Edit: now that I re-read that point in the docs that I re-found, sendHubCommand accepts a list of specifically a HubAction action object, I’m not entirely sure that a list of ‘just commands’ like I use (zigbee.something) will work, since I believe its expecting a list of new physicalgraph.device.HubAction objects.

Thanks for pointing this out, im not entirely sure now that sendHubCommand can actually run something that is not a HubAction object (and not sure how to call it as a zigbee, since most HubAction object examples are built for LAN talk).


Edit 2: well, I found an obscure reference in hubitat’s code to how to handle zigbee in a hubaction object (which doesn’t seem to be documented at all), which translated to smarthings something like this might work:

list commands = [command1, command2, command3]
cmds?.each { cmd -> actions << new physicalgraph.device.HubAction(cmd)}

sendHubCommand(actions)

Edit 3: I’ve also found this:

Zigbee Device called “Thing” - Devices & Integrations - SmartThings Community

which basically says to do this (its the Zigbee Dimmer DTH from ST):

sendHubCommand(zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, 0x00, "FE0000").collect { new physicalgraph.device.HubAction(it) }, 0)

So yes, sendHubCommand will never work (maybe, as it still is undocumented zone) if a hubAction object is not passed.


Edit 4: Even if the before stuff is true and the sendHubCommand might be wrong, it doesnt explain the reason why the scheduledRefresh method doesn’t run, So i still got no idea what exactly the problem is :frowning:

But now I got a more clear understanding of the sendHubCommand method at least, so the following code almost sure it works for the sendHubCommand, but doens’t fix the underlaying problem (haven’t fully tested it yet):

List refreshCommands = [
	zigbee.onOffRefresh(), // Poll for the on/off state
	zigbee.readAttribute(0x0002, 0x0000), // Poll for the temperature in INT16
	zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0002]), // Poll for the power usage in Watts (FLOAT4)
	zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0003]) // Poll for the energy usage in Kwh (FLOAT4)
]

refreshCommands.each {
	displayTraceLog("the it object is ${it}")
	def hubAction = new physicalgraph.device.HubAction(${it}, physicalgraph.device.Protocol.ZIGBEE)
	sendHubCommand(hubAction)
}

Our team hasn’t forgotten this situation, I notified them of the updates you made to this thread. Thank you for your patience.

1 Like

Thanks @nayelyz !

I’ve tried multiple combinations for some hours and I finally given up to try and understand what is wrong with the runEveryXXMinutes() methods.

Also, if its useful for someone, doing my testing I proved that the docs are either missing some clarification, or they are wrong, as sendHubCommand says it needs a HubAction object, but it seems it doesn’t (in line of what nayeliz mentioned earlier),


This doesn’t work:

void scheduledRefresh() {
	log.info "Running scheduled refresh command and requesting refresh values from device"

	List refreshCommands = []

	refreshCommands.add(new physicalgraph.device.HubAction(zigbee.onOffRefresh(), physicalgraph.device.Protocol.ZIGBEE))
	refreshCommands.add(new physicalgraph.device.HubAction(zigbee.readAttribute(0x0002, 0x0000), physicalgraph.device.Protocol.ZIGBEE))
	refreshCommands.add(new physicalgraph.device.HubAction(zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0002]), physicalgraph.device.Protocol.ZIGBEE))
	refreshCommands.add(new physicalgraph.device.HubAction(zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0003]), physicalgraph.device.Protocol.ZIGBEE))

	sendHubCommand(refreshCommands, 2000)
}

This works:

void scheduledRefresh() {
	log.info "Running scheduled refresh command and requesting refresh values from device"

	List refreshCommands = []

	refreshCommands.add(zigbee.onOffRefresh())
	refreshCommands.add(zigbee.readAttribute(0x0002, 0x0000))
	refreshCommands.add(zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0002]))
	refreshCommands.add(zigbee.readAttribute(0x000C, 0x0055, [destEndpoint: 0x0003]))

	sendHubCommand(refreshCommands, 2000)
}

Hi, @chares. Just following up, we haven’t been able to replicate the issue despite using the code you shared.
So, if the issue remains, please, open a support ticket by sending an email to build@smartthings.com and send the following info:

  • DTH current source code
  • Description of the behavior
1 Like

Huh, very strange, I did try multiple variations of the code, none worked, might be something on the APAC platform?

I’ll flick an email, thanks! :slight_smile: