Skip to content

Presenting Erroneous Accessory State to the User

Supereg edited this page Nov 16, 2022 · 14 revisions

TL;DR: Your accessory should always be reachable and fully operational.

HomeKit is designed around the philosophy that, if an accessory is reachable over the network (i.e. has published a HomeKit service), the accessory should work. This is analogous to the expectation that users would have with 'non-smart' devices, e.g. a light switch will always work, a door will always open when you push the handle, and a lock will always open with a key. This 'zero downtime' expectation is extremely important for smart home accessories to be viable alternatives to their 'dumb' equivalents, because the failure mode is so disastrous: for example, being able to unlock your front door less than 100% of the time means that, sooner or later, you will be stuck outside your house. One of the ways that Apple 'nudges' developers to create 'zero downtime' devices is by deliberately not offering any practical way for accessories to signal to HomeKit that they are temporarily not operational. (Prior to iOS 11, HomeKit did provide for an 'unreachable' flag, but this has now been removed.)

It is possible, inside a Characteristic's GET handler, to throw an error. This will usually force the Apple Home app UI to show 'No Response' for the accessory, but this 'No Response' state will generally persist for a long time (e.g. until the Apple Home app is closed and re-opened), even after you try to return a valid (non-error) value for the Characteristic, because once in a 'No Response' state, the Apple Home app no longer checks for characteristic updates. This leads to a poor UX, and is almost certainly not what you ever want to do to signal a transient error state.

For accessories to get Apple's MFi/HomeKit certification, they must be fully operational over the local network only. As such, accessory vendors have full end-to-end control and so, with careful design, can effectively guarantee 'zero downtime' and so there is no reason for a failure condition to exist. However, because many HAP-NodeJS/Homebridge accessories require a vendor's cloud service for device control (NB this would not pass HomeKit certification), there are sometimes circumstances where HAP-NodeJS's HomeKit service is accessible over the network, but the device may not be controllable.

So, first and foremost, when developing HAP-NodeJS accessories (or Homebridge plug-ins) try to control the device using a local API if at all possible. If this is not possible, plug-in developers should state clearly to their users that the plug-in relies on a cloud API, and so downtime is possible, to create realistic expectations. Also, please produce clear, human-readable output explaining what has happened in the Homebridge log if this situation occurs. (Unfortunately, almost no plug-ins actually do this.) In addition, exercise a high level of care when developing code to (1) avoid making blocking remote API calls in Characteristic GET/SET handlers, as this can lead to an unresponsive UI and HomeKit timeouts, (2) retry failed API calls if needed so that transient service issues e.g. 5xx error responses do not impact UX, and (3) make sure any possible errors are handled cleanly and do not lead to unhandled exceptions or unhandled promise rejections, which can result in Homebridge passing an error state back to HomeKit (or even shut down).

Below, we describe some mechanisms to signal a 'not ready' state to a user if there is absolutely no alternative.

Using dedicated, Accessory-specific characteristic values

Many services expose characteristics that can be used to signal a 'not ready' or 'fault' state (e.g. AirQuality characteristic has an unknown value, MotionSensor service has a StatusFault characteristic). If these are not available or suitable, return a 'safe default' state (for example, a sensor can return a not-detected state).

Using the StatusActive Characteristic

Almost all services support StatusActive as an optional characteristic. If StatusActive is set to false, this does not affect HomeKit's operation per se, but it will show 'Status Active - No' in the accessory's settings page in the Apple Home app. This will provide a visual cue to the user that the accessory is not operational.

Fatal (Permanent) Errors

In severe error cases that require user attention use HapStatusError with values of HAPStatus (e.g. service communication failure, most of the others have special meaning). Again please note that this will force the accessory into a 'No Response' state for an extended period of time (often until the Apple Home app is closed and re-opened; or generally put, when the Home App eventually retries to fetch data from the accessory), and so is almost certainly the wrong course of action, unless there is a permanent error condition that requires user action to rectify (e.g. authentication failure with the remote API service).