Hubitat is an event-driven system. In general, automations on Hubitat are performed using an app that acts on a device (the code behind a device is called a driver), where the device in Hubitat usually corresponds to some real-world device like a switch or thermostat. Devices may offer commands that can be executed on the device (e.g., to turn a switch off or set a thermostat to a certain setpoint). Devices generally also generate events, which are usually changes in state — for example, motion becoming active or a switch turning off.
Closely related to events are attributes, which have a value. These attributes generally represent the current state of a device and are visible under Current States on the device detail page. Changing the value of an attribute on a device is done by way of sending an event (normally in the driver). The hub itself — rather than a device — also generates some events, like at sunrise, sunset, or when the hub mode changes.
While not explicitly required, it is often the case that commands and attributes have a real-world relationship. For example, the on()
and off()
commands on a switch device are generally intended to turn the switch (or outlet, dimmer, bulb, etc.) on or off, which should result in an event that changes the value of the switch
attribute. However, note that this is not guaranteed. Exact behavior is dependent on the driver, but typically, you can assume a "fire-and-forget" approach: the driver sends the command to the device (asynchronously), then the driver will handle generating events in response if or when the device sends information about the new state back to the hub ("if" because the device may be offline, misconfigured, or another app may have sent a "conflicting" command before the device reports back, among other possibilities).
In general, an app might subscribe to one or more of these events, then perform user-configured actions in response. For example, an app could listen for (subscribe to) a specific motion sensor to becoming active (an event). The app could then tell a switch to turn on (run a command on a device). This is the means by which apps create automations on Hubitat.
Some apps are used to help integrate devices to make setup and maintenance easier than a driver-only approach or handle communication between the some real-world service or device and the Hubitat device(s). These are sometimes called "integration apps." Built-in examples include the Hue Bridge Integration app and the Sonos Integration app. The focus here is mostly on typical, automation-oriented apps.
For an introduction to Hubitat app development, start with App Development Overview and then move to Building a Simple App.
In general, a driver "translates" information to and from Zigbee, Z-Wave, Matter, a LAN or cloud API, or whatever the underlying device protocol is into "standard" Hubitat attributes (events) and commands, so both users and apps can interact with devices without knowing specific, underlying details.
NOTE: Virtual drivers are different in that they generally simulate real device behavior without an underlying real device. As a developer, they can be useful as a way to test apps without needing a "real" device (or real device events). Some users have also found creative uses for such devices in their automations.
An important concept for driver (and app) development is capabilities, which specify a set of commands and/or attributes that a driver that implements that capability should offer. Most drivers implement multiple capabilities, depending on the features of the device and its intended use.
For example, a driver that implements capability "Switch"
must offer on()
and off()
commands. Additionally, it must have a switch
attribute that reports either on
or off
as the value (generally expected to represent the actual device state), per the capability specification. Capabilities differ in what they require but generally correspond go real-world features that actual devices of that type generally offer. See Driver Capability List for a list of available capabilities and the commands and attributes they require.
For an introduction to Hubitat driver development, start with: Driver Development Overview.
Hubitat apps and drivers are written in Groovy and run inside the Hubitat Elevation app and driver execution environment. This environment is a sandbox that provides Hubitat-specific methods for convenience (see more in the other developer guides for apps, drivers, and common methods) and security.
As part of this model, there are some restrictions. You cannot:
println()
or sleep()
However, the sandbox generally provides a way to do anything you need to do in an app or driver. For example, you can use log.debug()
and similar methods instead of println()
.
You do not need to be a Groovy expert to create apps or drivers on Hubitat, though some development experience — especially with Groovy, Java, or similar languages — may be helpful. Hubitat uses Groovy version 2.4. Official Groovy 2.4 documentation can be found at: http://docs.groovy-lang.org/docs/groovy-2.4.21/html/documentation/. Other resources are also available online or in print (but be aware of what version they are written for, as different Groovy versions may not offer some features found in other versions).
There are also many Hubitat app and driver examples; see the app and driver developer docs and HubitatPublic repository for more). The rest of the developer documentation also includes examples and tutorials for app and driver development.
The app and driver development environment provides several built-in features (objects, methods, etc.) to facilitate development:
log
object with methods such as log.debug("My log entry")
to print text to Logs, which can be helpful for debugging during development (and as part of regular app or driver functionality in accordance with logging conventions, which you will learn more about)Apps and drivers can also access to other Hubitat-provided classes and methods as documented as well as many standard Groovy/Java classes and methods as documented above.
Some Groovy points that may be worth noting for developers familiar with other languages:
Groovy does not require semicolons at the end of statements, unlike Java/C/C++/etc., and Groovy developers typically omit them.
Groovy does not require parentheses when calling a method that has at least one parameter:
log.debug "My log message"
The return
keyword is optional; the last statement in a method will be used as the return value (if the method is not void
).
"Groovy strings" allow the use of ${}
surrounding a value to interpolate that value within the string (this does not work for single-quoted or triple-single-quoted strings):
String name = "John"
// This will print "My name is John" to Logs:
log.debug "My name is ${name}"
Many developers (and examples) make use of Groovy's support for closures. In summary, these are normally short, anonymous blocks of code similar to methods that can accept parameters (by default a single implicit parameter named it
) and return a value. They can be passed as parameters to methods and are particularly useful with many Groovy collection methods. For example:
def myList = [1,2,3]
log.debug myList.any { it == 1 } // returns true
or, as an example you might write in a Hubitat app:
myMotionSensors.any { it.currentValue("motion") == "active" }
Groovy supports positional parameters (like Java) but also named parameters. For example, when defining app preferences (inputs, etc.), you'll write lines of code like:
input name: "mySettingName", type: "text", title: "Write your text here"
This takes advantage of named parameters (and other Groovy features like optional omission of parentheses and semicolons) to allow this code to read almost like English. (Technically, named parameters are implemented as a Map
using the key as the name and value as the value in the method itself, but you rarely need to concern yourself with these details unless you intended to create such methods yourself. Many built-in Hubitat methods make use of this feature, and the examples here — and most real app and driver code you'll see — make extensive use of such calls.)
Groovy method and variable names conventionally use camelCase
(like Java).
input
) names given that these become app- or driver-wide variables automatically (you will learn more about this in the app and driver overviews).To get started developing apps, start with:
Or for drivers: