Your first two questions are, of course, related, and the one difference that you note is indeed that "Virtual..." drivers are intended to be used for standalone devices, whereas the "Generic Component..." drivers are meant to be used for child devices with a parent (often another driver but it could also be an app).
Virtual devices have varied uses, including testing apps (using "devices" you don't actually have), as workarounds to "convert" one device type type to another for easier use in certain apps without needing to mess with the original driver, as effectively global variables (a use that predates this feature but can still be used), or whatever else someone might want.
If you try to create a Generic Component device manually like a virtual device, you'll find it doesn't work and will give you errors in Logs with most commands. The reason is that it calls commands on the parent, and if you create one yourself, there will be none. Apps or drivers that create these devices have methods in them to handle calls from these drivers. Hubitat's drivers follow the format where calling, say, refresh()
on the Generic Component device will call componentRefresh()
on the parent device (passing the child device as a parameter as a way to know where it came from) -- and similar for other commands.
In the reverse direction, events are filtered down to the component devices from the parent by calling parse()
on the child/component device with a list of event maps. With a virtual driver, I suppose you'd have to call sendEvent()
directly on the child (I assume that works--but I haven't tried) or run commands to get the same result (e.g., on()
to make switch
be on
), which is pretty awkward. With virtual child devices used, you also run the "risk" of someone being able to call commands directly on the child device, like setTemperature()
on a component device that supports temperature reading, even when this doesn't make sense as it's likely just something reported by the device that should only be set on the child device by the parent (by way of the actual device data).
I have still seen virtual devices used in this case, perhaps because the author was not aware or there wasn't a Generic Component driver that worked--or maybe because the virtual features were actually desired for whatever reason (though in most cases I'd guess they are not).
For your last question, the isComponent
flag is set by the parent app or driver when creating the child. As you note, it prevents changing nearly anything on the child and prevents the user from deleting it (only the parent can). I think the idea is to set this true
when the child is an inextricable part of the parent, say some multi-endpoint device where each child represents a different endpoint (e.g., two outlets on a dual smart plug). In theory, I guess it can prevent accidents like the user deleting it or changing the driver and breaking the parent, In practice, my opinion is that it's usually more annoying than it's worth.
(And it prevents the user from deleting the device if they--and the parent driver--truly have no need for it.) YMMV.