Virtual Device Templates

Never hurts to ask.

Would it be possible to have a repo of the driver codes for the virtual devices?

I presume none of that would really be IP and it would give a good head start when trying to learn/develop custom drivers. Case in point, I'm trying to make a driver to track open contact sensors. Thinking it would be a good little project to sink my teeth into driver development. My thought was to create a "virtual contact sensor group" device, but I've been sitting here for 30 minutes bouncing between developer docs and searches in the forum just to figure out how to update the current state.

RECOMMENDED READING (while you wait)

1 Like

You could start with the Virtual Omni sensor code and delete what you don't want then add bells & whistles.


That's even better. I'll have to bookmark their repo.

Not every driver is published there, but there are a number of virtual device drivers in the HubitatPublic repo, which should be a pretty good starting point:

The biggest difference between virtual drivers would mostly be what capabilities you need to implement. That list is here: Driver Capability List | Hubitat Documentation. For a contact sensor, you'd probably want capability "ContactSensor" (and likely the do-nothing capability "Sensor" just for the sake of completenes--I'd say nearly any driver should have either that or "Actuator", both useful as generic selectors in rare but not unheard of situations).

The other differences fall out from that, mostly: what attributes you need, for example. You can see from the docs that capability "ContactSensor" should have a contact attribute, with possible values open or closed. In a "real" driver, you'd really just wait for those events to come in from the device (this traffic would come into a parse() method you'd need to define), but for virtual devices, you'd generally create custom commands to make the same effect. In Hubitat's virtual driver, these are called open() and close(), though you could really call them whatever you want. These are not a part of the capability because you do not need them for "real" devices. In some cases, like a virtual switch with capability "Switch", the on() and off() commands are required by the capability (because unlike sensors, actuators accept commands) and you'd generally just use those to manipulate the simulated state (in this case the switch attribute to a value of on or off).

A very simple virtual switch driver (coming soon to docs near you) might look like:

metadata {
   definition (name: "Custom Virtual Switch", namespace: "MyNamespace", author: "My Name") {
      capability "Actuator"
      capability "Switch"

   preferences {
      // none in this driver

def installed() {
   log.debug "installed()"

def updated() {
   log.debug "updated()"

def on() {
    // With a real device, you would normally send a Z-Wave/Zigbee/etc. command to the device here
    // For a virtual device, we are simply generating an event to make the "switch" attribute "on"
    // (with a real device, you would usually wait to hear back from the device in parse() before doing this)
    sendEvent(name: "switch", value: "on", descriptionText: "${device.displayName} switch is on")

def off() {
    // Same notes as for on() apply here...
    sendEvent(name: "switch", value: "off", descriptionText: "${device.displayName} switch is off")

For other kinds of devices, it's just a matter of modifying the capabilities, commands, and events (for attributes) as needed. That last thing brings us to this question:

This is what sendEvent() does in this virtual driver. An attribute state is the result of an event. The event name is the name of the attribute, and the event value is the new value of the attribute. Those are the two required parameters, though for others you'll see things like units (e.g., % or °F), type (physical vs. digital), and descriptionText (something like "[Device display name] [attribute name] is [attribute value] ([units])" and conventionally logged if descriptionText logging is enabled in the driver--not demonstrated above).

In related news, in case you aren't aware: what you're looking for cannot be done in a driver alone. A driver cannot talk to other devices (parent/child relationships excepted), so you'd need an app to tie your "real" devices into whatever state you want to track with this "virtual" device.


Par the course, @bertabcd1234, you've given me a lot to ingest. I did realize that I needed to use a sendEvent but was stumbling trying to figure out the proper way to call it. The omnisensor template did the trick.

I am aware on the event subscription piece. My thought was to use @dman2306 's sensor group app and see if I could get a count added to the virtual device that the app is using to reflect the group status. If I could get it working, I was going to hit up dman about adding it since he copyrights his stuff or getting permission to publish the updates since I don't think he's using Hubitat anymore.


Oh man...I did it!

1 Like