# Class SmartHome

java.lang.Object
com.codename1.home.SmartHome

public final class SmartHome extends Object

Entry point for the Codename One smart-home API -- reading the accessories in a user's home, reading and writing what they can do, watching them for change, running scenes, and adding new Matter accessories.

`getInstance()` never returns `null`. On a port with no smart-home support every operation fails fast with `HomeError.NOT_SUPPORTED` and every graph accessor returns an empty list, so calling code needs no platform-specific `if`.

##### Quick start

    SmartHome home = SmartHome.getInstance();
    // refresh() connects, and connecting is what prompts. Until it has run,
    // getAvailability() answers NOT_STARTED on iOS -- it cannot know what the
    // user decided without asking the platform, and asking is the prompt.
    home.refresh().onResult((structures, err) -> {
        if (err != null) {
            switch (home.getAvailability()) {
                case PERMISSION_REQUIRED:
                    home.requestAuthorization();
                    break;
                case PERMISSION_DENIED:
                    // Asked and refused. iOS never shows that prompt twice.
                    home.openHomeSettings();
                    break;
                case NOT_CONFIGURED:
                    home.openEcosystemApp();
                    break;
                default:
                    break;
            }
            return;
        }
        HomeStructure h = home.getPrimaryStructure();
        for (Accessory a : h.getAccessoriesSupporting(Trait.ON_OFF)) {
            addRow(a);
        }
    });

Turning a light on:

    AccessoryService svc = lamp.getPrimaryService();
    home.write(new TraitWrite(lamp, svc, Trait.ON_OFF, TraitValue.of(true)));

##### Three things that will surprise you

Android's default answer is not "available". With no extra setup an Android app can commission a Matter accessory and do nothing else: the graph is empty and no trait can be read or written. That is `HomeAvailability.COMMISSIONING_ONLY`, and it exists because reporting `AVAILABLE` would make the word mean something entirely different there than it does on iOS. The full graph needs the Google Home APIs, which need a Google Cloud project and a Home Developer Console registration only you can create; `getConfigurationProblems()` names what is missing.

Nothing wakes your app for an accessory change. HomeKit delivers changes only while your app is in the foreground, and the Google Home APIs need a live signed-in client -- the home hub, not your app, is what reacts to a sensor while the phone sleeps. Check `TraitSubscription.isPushDelivery()` rather than assuming; where it answers `false`, changes arrive when you call `drainChanges()` and at no other time.

A missing value is not zero and not an error. An accessory can legitimately have nothing to report -- an unmeasured temperature, a hue on a light that is currently in white mode. Ask `TraitReading.hasValue()` before `TraitReading.getValue()`; nothing in this API substitutes a zero for a measurement that was never taken.

##### Threading

Every method here may be called from the EDT and returns immediately. Every `AsyncResource` this class hands back resolves on the EDT, and every `HomeChangeListener` and `HomeStructureListener` delivery arrives on it -- on every platform, including the desktop, simulator and JavaScript ports, which marshal rather than answering on whichever thread happened to ask. A callback may touch components directly.

##### Platform support

- iOS, iPadOS, watchOS, tvOS, macOS -- HomeKit. Needs the `com.apple.developer.homekit` entitlement and an `ios.NSHomeKitUsageDescription` build hint; the build fails with an actionable message if the description is missing. Commissioning is iOS-only.
- Android -- Google Play services Matter commissioning, reported as `HomeAvailability.COMMISSIONING_ONLY`: an accessory can be added to the user's home, and there is no graph to read or control it through. The Google Home APIs that would provide one need a per-developer Cloud project and consent screen that cannot be shipped for you, and are not in this release.
- Simulator, desktop, JavaScript -- a local, app-private simulated home, reported as `HomeAvailability.LOCAL_ONLY`.

##### Not claimed in this release

Automations and triggers -- scenes only, and `isAutomationSupported()` answers `false` everywhere. Topology writes: creating homes, renaming rooms, moving accessories. Cameras and video. Security and alarm panels. Matter events, which is why `LockState.JAMMED` is unreachable outside HomeKit. Energy, appliance and diagnostic clusters. And Codename One is not a Matter controller -- everything Matter goes through the OS ecosystem, so the Apple Home or Google Home app has to be installed and set up.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `addStructureListener(HomeStructureListener listener)`
  Watches the home graph for topology changes.
  `boolean`
  `areIdsPersistent()`
  Whether accessory and structure identifiers survive an app restart.
  `AsyncResource<Scene>`
  `createScene(HomeStructure structure, String name, List<SceneAction> actions)`
  Creates a scene from a set of accessory states.
  `AsyncResource<Scene>`
  `deleteScene(Scene scene)`
  Deletes a scene.
  `static void`
  `deliverAuthorization(int requestId, int statusOrdinal, String error)`
  Answers `requestAuthorization()`.
  `static void`
  `deliverChanges(String subscriptionId, String[] lines)`
  Delivers watched trait changes.
  `static void`
  `deliverCommissioningResult(int requestId, String accessoryId, String accessoryName, String structureId, int commissionedToThisApp, String error)`
  Answers `Commissioner.commission(CommissioningRequest)`.
  `static void`
  `deliverDrained(int requestId, int deliveredCount, String error)`
  Answers `drainChanges()`.
  `static void`
  `deliverIdentifyResult(int requestId, String error)`
  Answers `identify(Accessory)`.
  `static void`
  `deliverReadings(int requestId, String[] lines, String error)`
  Answers `read(TraitReadRequest)`.
  `static void`
  `deliverRefreshed(int requestId, String error)`
  Answers `refresh()`.
  `static void`
  `deliverResyncRequired(String subscriptionId)`
  Tells subscribers that changes were missed and their values are stale.
  `static void`
  `deliverSceneResult(int requestId, String sceneLine, String structureId, String error)`
  Answers `executeScene(Scene)`, #createScene(HomeStructure, java.lang.String, java.util.List) and `deleteScene(Scene)`.
  `static void`
  `deliverStarted(int requestId, int availabilityOrdinal, String error)`
  Answers `refresh()`'s first call.
  `static void`
  `deliverWriteResults(int requestId, String[] lines, String error)`
  Answers `write(java.util.List)`.
  `AsyncResource<Integer>`
  `drainChanges()`
  Collects changes the platform has been holding and delivers them to the subscriptions that asked for them.
  `AsyncResource<Scene>`
  `executeScene(Scene scene)`
  Runs a scene.
  `Accessory`
  `findAccessory(String accessoryId)`
  One accessory by identifier, across every home.
  `HomeAuthorizationStatus`
  `getAuthorizationStatus()`
  Whether the user has granted this app access to their home.
  `HomeAvailability`
  `getAvailability()`
  Whether a home graph is usable right now, and when it is not, why.
  `HomeBackend`
  `getBackend()`
  Which platform service is behind this instance.
  `Commissioner`
  `getCommissioner()`
  Adding new Matter accessories.
  `List<String>`
  `getConfigurationProblems()`
  Build configuration this backend needs and does not have.
  `static SmartHome`
  `getInstance()`
  The smart-home API for this device.
  `int`
  `getMaxReadBatchSize()`
  The largest number of traits this backend reads in one call, or zero for no limit.
  `int`
  `getMaxWriteBatchSize()`
  The largest number of traits this backend writes in one call, or zero for no limit.
  `HomeStructure`
  `getPrimaryStructure()`
  The user's default home, as of the last `refresh()`.
  `List<HomeStructure>`
  `getStructures()`
  The homes, as of the last `refresh()`.
  `AsyncResource<Object>`
  `identify(Accessory accessory)`
  Asks an accessory to make itself known -- blink, beep, whatever it does.
  `boolean`
  `isAutomationSupported()`
  Whether this release can create or run automations -- a scene plus a trigger.
  `boolean`
  `isSupported()`
  Whether this device has any smart-home support at all.
  `static void`
  `notifyStructureChanged(int changeKindOrdinal, String structureId, String accessoryId)`
  Reports a change to the home graph.
  `boolean`
  `openEcosystemApp()`
  Opens the platform's ecosystem app -- Apple Home, Google Home.
  `boolean`
  `openHomeSettings()`
  Opens the system settings page where the user can change this app's smart-home access.
  `boolean`
  `openProviderSetup()`
  Opens wherever the user installs or updates the backend's provider.
  `AsyncResource<TraitReading>`
  `read(Accessory accessory, AccessoryService service, Trait trait)`
  Reads one trait, for the common case where a request builder would be noise.
  `AsyncResource<List<TraitReading>>`
  `read(TraitReadRequest request)`
  Reads trait values.
  `AsyncResource<List<HomeStructure>>`
  `refresh()`
  Loads the home graph from the platform.
  `void`
  `removeStructureListener(HomeStructureListener listener)`
  Stops watching the home graph.
  `AsyncResource<HomeAuthorizationStatus>`
  `requestAuthorization()`
  Prompts the user for access to their home.
  `TraitSubscription`
  `subscribe(SubscriptionRequest request, HomeChangeListener listener)`
  Watches traits for change.
  `AsyncResource<TraitWriteResult>`
  `write(TraitWrite write)`
  Writes one trait value.
  `AsyncResource<List<TraitWriteResult>>`
  `write(List<TraitWrite> writes)`
  Writes trait values.

  ### Methods inherited from class Object

  `clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### getInstance

    public static SmartHome getInstance()

    The smart-home API for this device.

    Never `null`. On a port with no support the returned instance answers `HomeAvailability.NOT_SUPPORTED` and every operation fails fast, so there is nothing to null-check and no platform branch to write.

    ###### Returns

    the instance, never `null`

  - ### isSupported

    public boolean isSupported()

    Whether this device has any smart-home support at all.

    A coarse question. `getAvailability()` is the one worth asking, because "supported" covers a device whose user has never opened the Home app and one whose every light is ready to switch.

    ###### Returns

    `true` when a backend is present

  - ### getAvailability

    public HomeAvailability getAvailability()

    Whether a home graph is usable right now, and when it is not, why.

    Check this before anything else, and branch through it rather than through platform detection.

    ###### Returns

    the availability, never `null`

  - ### getBackend

    public HomeBackend getBackend()

    Which platform service is behind this instance.

    For explaining a limitation to a user, not for branching on -- every real capability question has its own query. See `HomeBackend`.

    ###### Returns

    the backend, never `null`

  - ### getConfigurationProblems

    public List<String> getConfigurationProblems()

    Build configuration this backend needs and does not have.

    One sentence per problem, each naming the build hint that fixes it -- a missing `ios.NSHomeKitUsageDescription`, a missing `android.googleHome.projectId`. Empty when nothing is missing.

    This text is for you, not for your user. Nothing here can be fixed at runtime; it is a description of what the build left out. Log it, show it in a debug screen, and do not put it in front of someone holding a phone.

    ###### Returns

    an immutable list, possibly empty

  - ### areIdsPersistent

    public boolean areIdsPersistent()

    Whether accessory and structure identifiers survive an app restart.

    Both shipping backends answer `true`, so a favourite can be persisted by id. Ask before doing so anyway -- a local or test backend that regenerates its graph does not, and a favourites list that silently empties on every launch is a confusing bug to trace.

    ###### Returns

    `true` when identifiers are stable across launches

  - ### isAutomationSupported

    public boolean isAutomationSupported()

    Whether this release can create or run automations -- a scene plus a trigger.

    Always `false`. HomeKit, Google Home and Matter model triggers in three incompatible ways and Matter has none at all, so there is no honest common shape to expose. Scenes work everywhere; see `executeScene(Scene)`.

    The method exists rather than the capability being absent silently, so an app can say why the feature it wanted is not offered.

    ###### Returns

    `false`

  - ### getCommissioner

    public Commissioner getCommissioner()

    Adding new Matter accessories.

    Never `null`; ask `Commissioner.isSupported()` before offering an "add a device" button.

    ###### Returns

    the commissioner, never `null`

  - ### getAuthorizationStatus

    public HomeAuthorizationStatus getAuthorizationStatus()

    Whether the user has granted this app access to their home.

    ###### Returns

    the status, never `null`

  - ### requestAuthorization

    public AsyncResource<HomeAuthorizationStatus> requestAuthorization()

    Prompts the user for access to their home.

    Resolves when the platform's flow finishes, whatever the user chose -- so a resolved result means they were asked, not that they agreed. Read the status it carries.

    ###### Returns

    the resulting status, delivered on the EDT

  - ### openHomeSettings

    public boolean openHomeSettings()

    Opens the system settings page where the user can change this app's smart-home access.

    The only recovery from `HomeAuthorizationStatus.DENIED`: once the user has said no, the platform will not ask again from inside the app.

    ###### Returns

    `true` when something was opened

  - ### openEcosystemApp

    public boolean openEcosystemApp()

    Opens the platform's ecosystem app -- Apple Home, Google Home.

    The right answer to `HomeAvailability.NOT_CONFIGURED`: a user with no homes has to create one somewhere, and it is not here.

    ###### Returns

    `true` when the app was opened; `false` when it is not installed

  - ### openProviderSetup

    public boolean openProviderSetup()

    Opens wherever the user installs or updates the backend's provider.

    The recovery from `HomeAvailability.PROVIDER_NOT_INSTALLED` and `HomeAvailability.PROVIDER_UPDATE_REQUIRED`.

    ###### Returns

    `true` when something was opened

  - ### refresh

    public AsyncResource<List<HomeStructure>> refresh()

    Loads the home graph from the platform.

    Call this once before reading `getStructures()`, and again whenever a `HomeStructureListener` says the topology moved. It connects to the backend on first use, so it is also what triggers a permission prompt on a platform that defers one.

    ###### Returns

    the homes, delivered on the EDT

  - ### getStructures

    public List<HomeStructure> getStructures()

    The homes, as of the last `refresh()`.

    Empty until a refresh has completed -- this reads a cached snapshot and never calls into the platform, so it cannot block and cannot fail. See `Accessory` for why the graph is snapshots rather than live handles.

    ###### Returns

    an immutable list, possibly empty

  - ### getPrimaryStructure

    public HomeStructure getPrimaryStructure()

    The user's default home, as of the last `refresh()`.

    The one their ecosystem app opens. An app that only ever wants one home should use this rather than the first entry of `getStructures()`, which is in whatever order the platform gave them.

    ###### Returns

    the primary home, the first home when none is marked primary, or `null` when there are none

  - ### findAccessory

    public Accessory findAccessory(String accessoryId)

    One accessory by identifier, across every home.

    ###### Parameters

    - `accessoryId`: the identifier to look up, or `null`

    ###### Returns

    the accessory, or `null` when no home holds it

  - ### identify

    public AsyncResource<Object> identify(Accessory accessory)

    Asks an accessory to make itself known -- blink, beep, whatever it does.

    Best-effort: mandatory in Matter and present on HomeKit, but not reliably surfaced by the Google Home APIs, where it fails with `HomeError.TRAIT_NOT_SUPPORTED` rather than doing nothing quietly.

    ###### Parameters

    - `accessory`: the accessory to identify

    ###### Returns

    completion, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `accessory` is `null`

  - ### getMaxReadBatchSize

    public int getMaxReadBatchSize()

    The largest number of traits this backend reads in one call, or zero for no limit.

    Informational: `read(TraitReadRequest)` splits a larger request and recombines the answers, so there is no size a caller has to stay under.

    ###### Returns

    the batch limit, or zero

  - ### read

    public AsyncResource<List<TraitReading>> read(TraitReadRequest request)

    Reads trait values.

    One `TraitReading` per requested trait, in the order they were added. A partial success is the normal case: three values and one unreachable accessory resolve successfully, with the failure carried on that one reading. The resource itself fails only when the request never reached the platform.

    ###### Parameters

    - `request`: what to read

    ###### Returns

    the readings, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `request` is `null`

  - ### read

    public AsyncResource<TraitReading> read(Accessory accessory, AccessoryService service, Trait trait)

    Reads one trait, for the common case where a request builder would be noise.

    Prefer `read(TraitReadRequest)` when reading more than one: the cost of a read is the round trip to the platform, so four separate calls are four of them.

    ###### Parameters

    - `accessory`: the accessory to read

    - `service`: the service on it

    - `trait`: the trait to read

    ###### Returns

    the reading, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when any argument is `null`

  - ### getMaxWriteBatchSize

    public int getMaxWriteBatchSize()

    The largest number of traits this backend writes in one call, or zero for no limit.

    ###### Returns

    the batch limit, or zero

  - ### write

    public AsyncResource<List<TraitWriteResult>> write(List<TraitWrite> writes)

    Writes trait values.

    One `TraitWriteResult` per write, in order. A partial success is the normal case -- "turn off every light" against a home with a dead bulb mostly worked, and failing the whole operation would have the caller retry and flicker the house. The resource itself fails only when the request never reached the platform.

    ###### Parameters

    - `writes`: what to change

    ###### Returns

    the outcomes, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `writes` is `null` or holds a `null`

  - ### write

    public AsyncResource<TraitWriteResult> write(TraitWrite write)

    Writes one trait value.

    ###### Parameters

    - `write`: what to change

    ###### Returns

    the outcome, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `write` is `null`

  - ### subscribe

    public TraitSubscription subscribe(SubscriptionRequest request, HomeChangeListener listener)

    Watches traits for change.

    The handle comes back immediately; the platform registration happens behind it. Hold on to the handle and `TraitSubscription.stop()` it -- a dropped subscription keeps its listener reachable and keeps the platform delivering.

    Check `TraitSubscription.isPushDelivery()` on the result. Where it answers `false`, which is everywhere except HomeKit in the foreground, this listener will not fire until you call `drainChanges()`.

    ###### Parameters

    - `request`: what to watch

    - `listener`: where changes are delivered, on the EDT

    ###### Returns

    the subscription handle, never `null`

    ###### Throws

    - `IllegalArgumentException`: when either argument is `null`, or the request is empty

  - ### drainChanges

    public AsyncResource<Integer> drainChanges()

    Collects changes the platform has been holding and delivers them to the subscriptions that asked for them.

    The only way changes arrive on every backend except HomeKit in the foreground; see `TraitSubscription.isPushDelivery()`. Wire this into the point where your app comes to the foreground, and into whatever polling cadence suits what you are showing.

    ###### Returns

    how many changed readings were delivered, on the EDT

  - ### addStructureListener

    public void addStructureListener(HomeStructureListener listener)

    Watches the home graph for topology changes.

    Deliveries arrive on the EDT. This is about accessories appearing, disappearing, being renamed, moving room or changing reachability -- not about their values, which is what #subscribe(SubscriptionRequest, HomeChangeListener) is for.

    ###### Parameters

    - `listener`: the listener to add; `null` is ignored

  - ### removeStructureListener

    public void removeStructureListener(HomeStructureListener listener)

    Stops watching the home graph.

    ###### Parameters

    - `listener`: the listener to remove; `null` and unknown listeners are ignored

  - ### executeScene

    public AsyncResource<Scene> executeScene(Scene scene)

    Runs a scene.

    ###### Parameters

    - `scene`: the scene to run

    ###### Returns

    completion, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `scene` is `null`

  - ### createScene

    public AsyncResource<Scene> createScene(HomeStructure structure, String name, List<SceneAction> actions)

    Creates a scene from a set of accessory states.

    Check `HomeStructure.isSceneAuthoringSupported()` before offering this; several backends will run a scene and not author one.

    ###### Parameters

    - `structure`: the home to create it in

    - `name`: the scene's name

    - `actions`: what it should do; `TraitWrite.toSceneAction()` turns the changes a user just made into these

    ###### Returns

    the new scene, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when any argument is `null`, when the name is empty, or when there are no actions

  - ### deleteScene

    public AsyncResource<Scene> deleteScene(Scene scene)

    Deletes a scene.

    ###### Parameters

    - `scene`: the scene to delete

    ###### Returns

    completion, delivered on the EDT

    ###### Throws

    - `IllegalArgumentException`: when `scene` is `null`

  - ### deliverStarted

    public static void deliverStarted(int requestId, int availabilityOrdinal, String error)

    Answers `refresh()`'s first call. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `availabilityOrdinal`: the availability now that the backend is connected

    - `error`: the encoded failure, or `null` for success

  - ### deliverRefreshed

    public static void deliverRefreshed(int requestId, String error)

    Answers `refresh()`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `error`: the encoded failure, or `null` for success

  - ### deliverAuthorization

    public static void deliverAuthorization(int requestId, int statusOrdinal, String error)

    Answers `requestAuthorization()`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `statusOrdinal`: the resulting `HomeAuthorizationStatus` ordinal

    - `error`: the encoded failure, or `null` for success

  - ### deliverReadings

    public static void deliverReadings(int requestId, String[] lines, String error)

    Answers `read(TraitReadRequest)`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `lines`: one encoded reading per requested trait

    - `error`: the encoded failure, or `null` for success

  - ### deliverWriteResults

    public static void deliverWriteResults(int requestId, String[] lines, String error)

    Answers `write(java.util.List)`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `lines`: one outcome per write, as `accessoryId \t serviceId \t traitId \t applied \t errorName \t errorMessage`

    - `error`: the encoded failure, or `null` for success

  - ### deliverSceneResult

    public static void deliverSceneResult(int requestId, String sceneLine, String structureId, String error)

    Answers `executeScene(Scene)`, #createScene(HomeStructure, java.lang.String, java.util.List) and `deleteScene(Scene)`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `sceneLine`: the affected scene, encoded as in `HomeBridge#getScenes(java.lang.String)`, or `null`

    - `structureId`: the home the scene belongs to

    - `error`: the encoded failure, or `null` for success

  - ### deliverCommissioningResult

    public static void deliverCommissioningResult(int requestId, String accessoryId, String accessoryName, String structureId, int commissionedToThisApp, String error)

    Answers `Commissioner.commission(CommissioningRequest)`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `accessoryId`: the new accessory, or `null`

    - `accessoryName`: the name it ended up with, or `null`

    - `structureId`: the home it joined, or `null`

    - `commissionedToThisApp`: `1` when this app can address it

    - `error`: the encoded failure, or `null` for success

  - ### deliverIdentifyResult

    public static void deliverIdentifyResult(int requestId, String error)

    Answers `identify(Accessory)`. Called by the ports from any thread.

    ###### Parameters

    - `requestId`: the request being answered

    - `error`: the encoded failure, or `null` for success

  - ### deliverDrained

    public static void deliverDrained(int requestId, int deliveredCount, String error)

    Answers `drainChanges()`. Called by the ports from any thread, after every [#deliverChanges(java.lang.String, java.lang.String[])] the drain produced.

    ###### Parameters

    - `requestId`: the request being answered

    - `deliveredCount`: how many readings were handed over

    - `error`: the encoded failure, or `null` for success

  - ### deliverChanges

    public static void deliverChanges(String subscriptionId, String[] lines)

    Delivers watched trait changes. Called by the ports from any thread, unsolicited or in response to a drain.

    ###### Parameters

    - `subscriptionId`: which subscription these belong to

    - `lines`: the encoded readings

  - ### deliverResyncRequired

    public static void deliverResyncRequired(String subscriptionId)

    Tells subscribers that changes were missed and their values are stale.

    ###### Parameters

    - `subscriptionId`: which subscription lost its stream

  - ### notifyStructureChanged

    public static void notifyStructureChanged(int changeKindOrdinal, String structureId, String accessoryId)

    Reports a change to the home graph. Called by the ports from any thread.

    ###### Parameters

    - `changeKindOrdinal`: the `StructureChangeKind` ordinal

    - `structureId`: the home affected, or `null`

    - `accessoryId`: the accessory affected, or `null`