All,
I am in the process of trying to rework some existing code. Is somthing like this possible in an app?
// Initialize within the app
def initialize(){
unsubscribe()
subscribe(this, "myEvent", "myEventHander")
}
// generateEvent callback
def generateEvent(data){
sendEvent(name: "myEvent", value: data)
}
// Used to process the event
def myEventHander(evt){
// Do some work here
}
Instead of doing a callback from a child object I was hoping to use sendEvent within the app via the generateEvent callback, this way it would return immediately and myEventHander would run in the background.
sendEvent is part of the driver object, so it needs to be called in the driver, since it sets a driver attribute state. You just need to call a method in the driver that accepts the data as a parameter and then does the sendEvent.
Your subscription to that event in the app will fire from the event you create when the driver does the sendEvent after you call the method in the driver to send it from the app.
Is there a reason you would rather have it send an event back that is subscribed to by the parent, instead of just having the child call a parent method to set the value? It is not clear what the use case is here.
Yes, because I didn't want to have to subscribe to every child device's events because I wasn't sure how efficiently it's handled by Hubitat. If there is no efficiency concern, then that is probably the best way to go. I also didn't want the blocking nature of traditional callbacks waiting for the action to complete. So my idea was to centralize the sendEvent in a wrapper function eg. sendSpecialEvent(with parameters here) so you don't need to know how to format the event. I suppose I could offload this function into library and include it in the drivers which would also centralize the sendSpecialEvent function.
In my driver sendEvent methods, which are usually command methods to set a value, if I need the parent to know the value I just call a method in the parent from that driver method, as well as doing the sendEvent in the driver. When anything sets the value via the driver method that does the sendEvent, the parent method is called to let it know the value changed, instead of the app using a subscription to see the change resulting from the sendEvent.
Maybe I'm not following you... by format the event you mean the type of value the attribute accepts? If you use do not define the type for the method parameter, it should take what you give it and send the event as the type that was received from what you sent for the value to the method. So you can have a sendAnEvent(name, value) method that just sends the name and value sent to it, assuming you sent the correct type for the attribute name being updated with the correct sendEvent value type.
You don't have to define a command. You can actually generate events this way from a device and have the parent listen for eg. "linkEvent".
def sendLinkEvent(String eventSource, String eventType, int networkId, int sourceId, int linkId) {
logTrace "sendLinkEvent(eventSource: ${eventSource}, eventType: ${eventType}, networkId: ${networkId}, sourceId: ${sourceId}, linkId: ${linkId})"
def eventData = [
eventSource: eventSource,
eventType: eventType,
networkId: networkId,
sourceId: sourceId,
linkId: linkId,
]
sendEvent(name: "linkEvent", value: new groovy.json.JsonOutput().toJson(eventData), isStateChange: true)
}
The parent subscribes to "linkEvent" on the device and when that occurs it routes those linkEvents to the virtual devices which need to respond to the event. The other option I had was not using sendEvent but just performing a callback to the parent which basically does the same thing.
The benefit of sendEvent is it's asynchronous (from what I can tell) thus not blocking the caller until the processing is completed.
Ah, I never thought about the async with a sendEvent vs a parent method call, and I didn't realize calling parent will block the caller until it completes. This is nothing that has been an issue for me, but I guess in the right situation a sendEvent vs parent call could be useful.
I've done similar to your linkedEvent with devices that are not children of the app, but with children I have always just called the parent method and in my case I've never had an issue waiting for the parent to finish processing that causes an issue. The call to the parent method is usually the last thing I do in the method so nothing is waiting for it.
I solved the problem via runIn from the raw socket device. When it's a response to other devices it's dispatched without blocking the driver.
runIn(0, "asyncParseMessageReport", [data: [messageData: messageData]])
This allows the inbound message data from the network to be dispatched in a separate thread for the callbacks. Doing this allowed me to get the same result as sendEvent without the serializing to JSON and deserializing.
I also wanted to avoid message queues with semaphores or mutex but I may need to take that route later. Right now all of my tests show it works quite well.
2 Likes