Generic Component devices or Virtual devices (and isComponent flag)

Ive been writing device handlers for a while but Ive just realised some of my early device handlers use 'Virtual X' children rather than 'Generic Component X' children. Ive been trying to understand the differences and reasoning for picking one over the other, so thought I'd check my understanding was correct:

  • Virtual X (e.g. Virtual Contact Sensor) - this is designed so anybody can easily create a device and allocate it to something else using Rule Machine or similar, but generally not built for integrating into other drivers/apps. This device allows you to manually set the status in the control panel (i.e. controlled without having to do it through a parent device).
  • Generic Component X (e.g. Generic Component Contact Sensor) - this is primarily designed for parent:child interactions and offers limited control of the device. This clearly isnt true since a 'Generic Component Switch' does allow control without needing the parent - if so how are they different from a Virtual Switch?
  • isComponent=true flag - this is a sub setting of the 'Generic Component' devices that prevents the device being deleted (must be done by a parent), and prevents the handler being changed to something else.

What other differences are there and why? Is some of it just a remnant of SmartThings naming?
Thanks!

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. :slight_smile: (And it prevents the user from deleting the device if they--and the parent driver--truly have no need for it.) YMMV.

2 Likes

Hi, I have installed a tuya (smartlife) integration and it use generic devices as child like you describe it. Its working fine but the scene are not 100% considered as switches. They trigger scene but the status stay blank. Its fine but I would like to use those devices in webcore with events. The problem is that those devices seems not to send an update status or something, so webcore never get any change event on it. All other switches devices works great so its inside the driver that the update is not implemented... I try to add some line in the driver to just send a status change or something when the ON button is pressed... Do you know where I could find help on this? I not a dev so I m not sure where to start! thanks!

I am not familiar with those drivers, and I don't think this issue is related to the original question aside from possibly using generic component drivers (as many built-in and community drivers do) for child devices. There is usually a thread/topic here where devs post about releases for their drivers/apps and where users can go to get help, so there is probably one for the integration you are using. I might try starting there if you haven't already.

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