Best Practices (for Developers)
A collection of best practices and other tips for Hubitat app and driver developers
Overview
In general, all documentation (here) aims to demonstrate what we believe to be good development practices for Hubitat Elevation apps and drivers. For example, particularly if you are new to development on the Hubitat Elevation platform, see:
- Developer Overview General overview of Hubitat development environment
- App Overview Getting started with app development
- Driver Overview Getting started with driver development
However, even developers with experience on the Hubitat Elevation platform may wish to (re)read these documents, particularly the app and driver overviews and the more advanced documentation they link to, for the above reason.
However, not all Hubitat Elevation app and driver code you may encounter "in the wild" follows our demonstrated practices. This is not necessarily wrong; there are a variety of programming styles and preferences, and programming itself is often described as both a science and an art. Still, we are occasionally asked about this subject, and there are certain Hubitat features that we consider best used in certain ways for either user experience or efficiency in the app and driver execution environment.
We present some of these tips here, though this is by no means a complete list. The Community forum is a great place to connect with other developers (and staff!) to learn more.
Storing Data
- Concerns: efficiency
Apps and drivers have the state object available to persist data between executions (see, for example, App Overview: Storing Data [state]). This is convenient and works well for small amounts of data. But note that this data is serialized and deserialized to/from JSON on or after (or for atomicState during) app or driver execution. Large amounts of data may be best stored more efficiently using another technique, including:
- File Manager files (see: File Manager API)
- A
static @groovy.transform.Fieldvariable defined at the top level of your app or driver code- Note that these are shared among all instances of the app or driver (so drivers may want to use a
ConcurrentHashMapwith a unique key such as the device ID, then store data for that particular instance inside the value for that key; and apps that permit multiple instances may want to do something similar — unless this is not a concern for that driver or app) - Note also that these values are not persisted across a hub reboot or re-save of app or driver code (so it may need to be combined with a different method for persistent storage if that is a desired outcome, but it could work well as-is for caching, etc.)
- Note that these are shared among all instances of the app or driver (so drivers may want to use a
Attributes vs. State
- Concerns: efficiency, user experience
Device attributes (shown under "Current States" on the device detail page) and state (or atomicState) both offer places to store values. However, in general:
- Attributes (or events, the way by which attribute values are updated) should be used in cases where the user may wish to create an automation based on a change in this value
- Typical examples include a real-world aspect of the device changing, like a switch turning on or off, a sensor changing temperature, etc.
- Changing the value of an attribute (usually) generates an event, which apps can subscribe to and wake if subscribed — what makes them usable for automation, and which may have slightly more overhead than the below
state(or another method of storage) should be considered for other cases- This typically includes data used only internally by the app or driver (and that needs to be stored between executions)
Logging
- Concerns: user experience
We suggest following logging conventions as outlined in the above overviews. Writing log entries, in general, has little chance of affecting hub performance. However, many users prefer to control if or how apps and drivers create log entries and for what types of information.
Explicit types vs. def
- Concerns: efficiency
While many apps and drivers are small enough or executed infrequently enough that using def as many Groovy developers do is not a concern, some developers of larger apps have reported possible improvements in speed or memory usage when using specific types (or void if appropriate) for variables, methods, etc. For example,
void appButtonHandler(String buttonName) {
// ...
}
may be preferable to:
def appButtonHandler(buttonName) {
// ...
}
Other
This document is not an exhaustive list of all possible concerns, nor is it possible to create such a document given the vast flexibility offered by Groovy and the Hubitat development environment. However, we hope that the above information is helpful, aim to add to the document over time, and encourage browsing the Community forum to connect with other developers and staff to continue learning more.