Subscribe() on Device to child Device

Is the subscribe() method supported on a device such that, for example, a parent device can subscribe to events from one of its children?

Subscriptions are only available for applications, not devices.
The parent child device pattern is the child calls parent.someMethod(params) and in the parent device you have someMethod(params) defined

1 Like

Thanks...but bummer - would be easier to use generic child devices if a parent device could subscribe the events from them. Otherwise, each child needs to be custom built to callback to the parent to report state changes. Granted this could all be wrapped in an application instead of a parent device and maybe that's the preferred approach on Hubitat (this is a borrowed paradigm from ST). My design pattern is a composite device that has many individual components as its children.

We have a suite of these already, examples can be found here:

Take a look at the model used for Hubduino if you want something to reference. One parent device can have MANY devices each with it's own capabilities and @ogiewon's drivers are rock-solid. And he's a nice guy so I'm sure he'd answer questions if you had em.

The parent/child device interface accomplishes two primary goals.
Affords multi-endpoint duplicate capability publishing without the need to resort to custom commands, ie on2(), off(2)
And allows child and parent to execute each others methods without involving the event system proper.
In the big scheme of things a subscription involves publishing an event, this is a much more intense operation and isn't really appropriate for child parent device communications.

Yeah I have been through that code to get some ideas and it was very good. I was after the idea of just using a "Virtual Switch" as a child component instead of writing a custom Driver for the child. I think @mike.maxwell captured it with his examples of Generic Component devices and I see these are installed on the platform so custom driver developers can just use those and then handle the calls on the parent based on the passed in device.

1 Like

I don't mean to hijack the thread...but one quick question. All messages are parsed and sent by the parent, correct? The parent/child thing is still one that I struggle with a little. I've started several drivers in that model and I always end up giving up.

For example, your example has a switch and a dimmer on the parent. But how would you handle this with multiple dimmers on the same parent? That's where I always end up in the weeds.

Yes, The parent is the one that carries out the actual sending of commands and parsing of the payloads that are returned by the actual physical device, be that lan, zigbee, zwave or whatever...
The parent is then responsible for figuring out which child device to send the parsed events to, the events can be sent using getChildDevice(dni).sendEvent(yada...), or via the method being used in the repo examples.
The parent also needs to implement the child methods that are called from the child device, and convert these into physical device specific commands, no different than any other driver...
The examples posted pass the child device wrapper into the parent method, this allows you to figure out which child (in the case of a multi endpoint switch for example) is calling the method.
If you only have one child device it doesn't matter, but if you have three composite switches hanging off of the parent you need to know so you can create the correct command to send to the real device...

Lets take an example for two switches, both zigbee and zwave have endpoint ids that are returned in payloads from multi endpoint devices, I use the endpoint id and the parent device id as the child device dni when I create the composite device, ie childDni = "${device.id}:${epId}".
So when a payload comes in to the parent from endpoint 01 I know who to generate the event for, I fetch the child device using getChildDevice("${device.id}:${epId}") then send the event.
figuring out which child called the parents implementation of the child command is done in a similar way, since the device wrapper is passed into the parent method, the child device attributes are available in the parent, in the example we have the componentOn(cd) method, the endpoint id that was used when creating this device can be extracted thus epId = cd.deviceNetworkId.split(":")[1], and now we know which endpoint to generate a command for.

1 Like

This maps well onto my work - in may case I'm working on my Pentair Pool Controller code. That will map 8, 16 or more switches which control individual circuits on the pool controller. There are also a collection of buttons to activate various lighting modes, and separate switches to control pumps, valves, etc.

On the ST platform version of my driver, I had separate custom child devices for all these items. But that was really required only because I had to implement the UI of each child and they needed to look different. The actual behavior is all simple...its just a "switch" or a button for many of them. On Hubitat, since we split out the device behavior from the UI, I can potentially eliminate a bunch of custom code which will simplify installation and maintenance. All at the expense of a bunch of refactoring from my ST port of course :slight_smile:

1 Like

Thanks so much Mike. That does clear up a lot. It's so close I can taste it! LOL I'm sure I'll get it eventually. Thanks!!!

1 Like

One more thing @mike.maxwell - I am curious why the example code uses this approach using the parse method:

cd.parse([[name:"temperature", value:value, descriptionText:"${cd.displayName} temperature is ${value}${unit}", unit: unit]])

instead of something like:

cd.sendEvent(name: "temperature", value: value)

The former allows the logging options to be controlled by the child preference setting, the latter does not.
Also the former limits events to known attribute names per the child drivers capability definition, you can see this in the example drivers.

One does not need to use the parse(map) method for sending events, that is the method we use internally, and you of course can use either method.

1 Like