Apps on Hubitat Elevation are the means by which users configure automations. Hubitat comes with several built-in apps (e.g., Room Lighting, Button Controller, Notifications, and more), but it is also possible to write user apps, also known as custom apps. Writing user apps is the focus of this document.
To create or modify a user app, navigate to Apps Code in the Hubitat web interface (expand the Developer Tools section if you do not see this in the sidebar). Select New App to create a new app, or select an existing app in the list to modify that app. When finished, select Save to commit your changes. Note that only valid code will save; any errors will appear in a a yellow bar at the top of the editor.
For an overview of the Hubitat development environment in general, see Developer Overview if you have not already read it.
The code below is a fully complete Hubitat app, though it doesn't do much besides display "Hello, Hubitat!". Still, it demonstrates all components necessary for a basic app. Try adding it to Apps Code and installing it under Apps > Add User App to see how it works!
definition(
name: "My First App",
namespace: "MyNamespace",
author: "My Name",
description: "A simple example app",
category: "Convenience",
iconUrl: "",
iconX2Url: ""
)
preferences {
page(name: "mainPage", title: "My Page", install: true, uninstall: true) {
section {
paragraph "Hello, Hubitat!"
}
}
}
// Called when app first installed
def installed() {
// for now, just write entry to "Logs" when it happens:
log.trace "installed()"
}
// Called when user presses "Done" button in app
def updated() {
log.trace "updated()"
}
// Called when app uninstalled
def uninstalled() {
log.trace "uninstalled()"
// Most apps would not need to do anything here
}
Further explanation of these components is provided below.
The definition()
in an app contains data about the app, including the name and description (shown on the Add User App page). Required parameters:
name
: The name of the app as will be displayed in the Add User App dialog, the App Status page, and other locationsnamespace
: A unique identifier for the developer (app name plus namespace combinations must be unique), typically a username typically used by that developer (often their GitHub username)author
: A string identifying the developer, sometimes a real name or company name (unlike the name and namespace, this is used only for display purposes)description
: A short description of the app's purpose (shown under the app name on the Add User App page)category
: currently not usediconUrl
: currently not used; should be set to empty stringiconX2Url
: currently not used; should be set to empty stringFor more, see App Definition.
The preferences
block defines the user interface for the app. The preferences
block should consist of one or more page
blocks. Each page
consists of one or more section
blocks. The bulk of the user interface — the ability to select devices, type in or select other options, display paragraphs, etc. — is done inside a section
.
All page
parameters are optional:
name
: displays at top of pagenextPage
: for multi-page apps, should be set to the name
of the page to navigate to when user selects the Next buttonuninstall
: if true
, will show Remove (App Name) button at bottom of this page (should be set to false
only for multi-page apps and true
on at least one page)install
: if true
, will show button at bottom of page to install app if not already installed (e.g., user just added new app)Pages must contain one or more sections (which in turn contain inputs, paragraphs, etc., as below). Most commonly, a section is defined like:
section("My Section Title") {
// inputs, etc. here
}
For a full description of app preferences, pages, sections, inputs, and related features, see: App Preferences.
The input()
method method allows you to display various types of inputs (device selectors, string or numeric inputs, etc.). The general format is:
input(String name: elementName, String type: elementType, String title: elementTitle, /* ... additional options */ )
Groovy allows omitting the parentheses, and the name
and type
labels can be omitted, allowing for simpler calls like this:
input "myName", "myType", title: "My Input"
The value users provide for all input
fields is saved in to a Map
built in to each instance of the app called settings
with the key being the name
of the input
. These values can be accessed in the same way as values for any map, e.g., settings.myName
or settings["myName"]
. Inputs can also be accessed in the app by using simply the name
(as if it were a field/variable), e.g., myName
.
There is also one hub-provided setting name that is populated in certain cases and should be avoided when naming your own inputs:
hubitatQueryString
: JSON representation of parameter names/values provided in the URL when the app page is loaded (i.e., after the ?
a URL), if any (none by default)For more on app inputs and other preferences methods, see: App Preferences.
Apps are not always "running." An app will wake in response to certain events, including:
subscribe()
(this will run the developer-specified callback method) – this is the most common reason a typical app created to perform an automation would wakerunIn()
or similar methods (this will run the developer-specified callback method)preferences
)installed()
, updated()
, or uninstalled()
)mappings
, described elsewhere)To store data between executions, apps have a built-in state
object available. This object behaves like a Map
and allows storing most common data types (strings, numbers, Lists or Maps of such objects, etc. — anything that can be serialized to/from JSON). Each installed instance of an app has its own state
object.
A similar object named atomicState
is also available. They work similarly, except that state
writes data just before the app goes to sleep again, whereas atomicState
commits the changes as soon as they are made. We suggest beginning with state
and changing to atomicState
only if there are concerns for simultaneous execution and state data related to this. Use of state
is more efficient. Another more efficient alternative to atomicState
is to use specify singleThreaded: true
in definition
, preventing more than one overlapping wake of the app.
The atomicState
object also provides a convenience method:
atomicState.updateMapValue(Object stateKey, Object key, Object value)
: gets a Map
from atomicState
, updates a value inside that map, and writes it backThe
state
andatomicState
objects refer to the same data and can be mixed in the same app if needed, but most developers choose one or the other based on the requirements of the app.
Example: state.foo = "bar"
An app should contain at least the hub-required callbak methods:
installed()
: called once when the app is first installedupdated()
: called when user selects Done button in appuninstalled()
: called when app is uninstalled. Most apps will not need to do anything here (e.g., subscriptions, etc. are automatically removed), but if your app integrates with an external system that should be notified, this may provide an option.The rest of the app can contain user-defined methods (as you may need to help organize your code), subscription event or scheduled job handlers (if needed), or other methods as may be necessary for your specific app.
As hinted at in the demo, apps (and drivers) have a built-in log
object that can be used to write entries to Logs. Available methods are:
log.info
log.debug
log.trace
log.warn
log.error
These will tag the log entry in Logs with the specified "level," e.g., a white "info" box next to the log entry, a blue "debug" box or a red "error" box. Info and debug logs are the most commonly used type across apps and drivers. Many developers find logging helpful as a tool for debugging or other troubleshooting when developing an app (and may use far more during initial development than would be found in a more stable app).
Example: log.debug "The value of state.foo is ${state.foo}"
To get started building a simple app — one that actually response to device events and sends commands to devices — continue with:
It may also be helpful to look at example apps, many of which can be found in the HubitatPublic GitHub repository: https://github.com/hubitat/HubitatPublic/tree/master/example-apps.
The remaining developer documentation provides more information on what methods are available for app (and driver) code and what methods and properties are available on Hubitat-provided objects or utilities. For apps, the following may be particularly helpful: