# Upgrading to SDK v3

Homey 5.0.0 introduces a new SDK version. This SDK version removes the dual callback/promise support for API's and allows you to use `async/await` everywhere you want/need to. We hope supporting asynchronous calls everywhere will make it easier to develop and maintain Homey apps.

You can start using SDK v3 simply by updating the `sdk` property in your App Manifest from `2` to `3`. Since SDK v3 is introduced in Homey 5.0.0 the Homey `compatibility` field in your App Manifest should be changed to `>=5.0.0`.

{% code title="/.homeycompose/app.json" %}

```javascript
  "compatibility": ">=5.0.0"
```

{% endcode %}

{% hint style="warning" %}
With the introduction of a new SDK version we have discontinued the support for SDK version 1. Apps using SDK version 1 will be disabled starting with Homey 5.0.0.
{% endhint %}

Note that the SDK v2 versions of the Homey app libraries (`homey-oauth2app`, `homey-rfdriver`, `homey-meshdriver` and `homey-log`) are not compatible with SDK v3. Make sure to upgrade these libraries to versions that do support SDK v3. When using `homey-meshdriver` for Zigbee or Z-Wave, please look at the specific sections for [Z-Wave](#z-wave-and-meshdriver) and [Zigbee](#zigbee-and-meshdriver).

## Homey instance moved from `require('homey')` to `this.homey`

In previous versions of the SDK you could access the Homey API through `const Homey = require('homey')`. This is a very convenient way to give access to the managers anywhere it is needed however this prevents us from optimizing apps in the future as it pollutes the "global namespace". Therefore, the homey module now only exports the classes you might need such as `Homey.App`, `Homey.Driver` and `Homey.Device`. The module also contains your environment variables through `Homey.env`, and app manifest through `Homey.manifest`. All managers (the API you use to interact with Homey) have moved to `this.homey`, a property that is set on your App, Driver and Device instances. You can find the name of your manager in the `Homey` instance documentation.

Additionally, it is no longer needed to create and register resources. For example previously you would write the following code:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require("homey");

class Driver extends Homey.Driver {
  onInit() {
    this.rainingCondition = new Homey.FlowCardCondition("is_raining");
    this.rainingCondition.register();

    this.myImage = new Homey.Image();
    this.myImage.setUrl("https://www.example.com/image.png");
    this.myImage.register().catch(this.error);
  }
}

module.exports = Driver;
```

{% endcode %}

In SDK v3 this turns into:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require("homey");

class Driver extends Homey.Driver {
  async onInit() {
    this.rainingCondition = this.homey.flow.getConditionCard("is_raining");

    this.myImage = await this.homey.images.createImage();
    this.myImage.setUrl("https://www.example.com/image.png");
  }
}

module.exports = Driver;
```

{% endcode %}

{% hint style="warning" %}
For the same reason we have moved away from "polluting the global namespace", we urge you to define all variables as properties on your App, Driver or Device instances. Your global scope (anything that isn't inside of a class) should only contain constants (their values shouldn't be changed). Relying on global state may cause issues for your app in the future.
{% endhint %}

## Creating and triggering Flow cards

Since most apps supports custom Flow cards, below is an example of how to register and trigger Flow cards in SDK v3. See [Flow](https://apps.developer.homey.app/the-basics/flow) section for more details.

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require("homey");

class Driver extends Homey.Driver {
  async onInit() {
    // Register a Flow card to trigger a notification on a TV
    this.homey.flow
      .getActionCard("show_notification")
      .registerRunListener(async (args) => {
        return args.tv.createToast(args.message);
      });

    // Register a Device Flow card to launch an application on a TV
    this._flowTriggerAppLaunched = this.homey.flow
      .getDeviceTriggerCard("app_launched")
      .registerRunListener(async (args, state) => {
        return args.application.id === state.id;
      });

    // Register an autocomplete listener for the `application` argument of the `app_launched` flow card
    this._flowTriggerAppLaunched.registerArgumentAutocompleteListener(
      "application",
      async (query, args) => {
        return args.tv.autocompleteApplicationArgument(query);
      }
    );
  }

  // Trigger Flow's using the `app_lauched` card
  triggerAppLaunchedFlow(device, tokens, state) {
    this._flowTriggerAppLaunched
      .trigger(device, tokens, state)
      .catch(this.error);
  }
}

module.exports = Driver;
```

{% endcode %}

## Web API improvements

API routes now need to be defined in the App Manifest instead of the `api.js`. Additionally since you can no longer gain access to the API through require-ing homey it is now passed as an argument to your API handler method. Read more about the Homey App Web API in the [Web API guide](https://apps.developer.homey.app/advanced/web-api). Here is an example of the new structure for Homey App Web API's:

{% code title="/.homeycompose/app.json" %}

```javascript
  "api": {
    "getSomething": {
      "method": "get",
      "path": "/"
    },
  }
```

{% endcode %}

{% code title="/api.js" %}

```javascript
module.exports = {
  async getSomething({ homey, query }) {
    const result = await homey.app.getSomething();
    // ...
    return result;
  },
};
```

{% endcode %}

## Consistent APIs

We simplified some APIs with the goal of making them more consistent. This is to say we removed `Driver.getManifest()` and `Device.getDriver()` in favour of using properties: [`Driver#manifest`](https://apps-sdk-v3.developer.homey.app/Driver.html#manifest) and [`Device#driver`](https://apps-sdk-v3.developer.homey.app/Device.html#driver).

Additionally, the signature of [`Device#onSettings()`](https://apps-sdk-v3.developer.homey.app/Device.html#onSettings) has been changed to support destructuring: `onSettings({ oldSettings, newSettings, changedKeys })`.

## Promise-only APIs

In previous versions of the Apps SDK many methods supported both callbacks and Promises. In version 3, the support for callbacks has been removed from all places that previously supported both. You can reference the [SDK API documentation](https://apps-sdk-v3.developer.homey.app) to find the signature of all methods.

### Async pairing socket

The argument that is passed to [`Driver#onPair()`](https://apps-sdk-v3.developer.homey.app/Driver.html#onPair) has been changed. Previously this argument was an `EventEmitter` with an `.on` method that would receive a callback as its last argument. In order to offer better support for promises this was changed to a `PairSession` that has a `.setHandler` method where it is possible to return a Promise.

In SDK v2 you might have implemented an `onPair` method like this:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require('homey');

class Driver extends Homey.Driver {
  onPair(session) {
    socket.on("my_event", (data, callback) => {
      this.log("data", data);

      callback(null, "reply");
    });
  }
}

module.exports = Driver;
```

{% endcode %}

In SDK v3 this turns into:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require('homey');

class Driver extends Homey.Driver {
  onPair(session) {
    session.setHandler("my_event", async (data) => {
      this.log("data", data);

      return "reply";
    });
  }
}

module.exports = Driver;
```

{% endcode %}

This also affects [`Driver#onPairListDevices()`](https://apps-sdk-v3.developer.homey.app/Driver.html#onPairListDevices). If you're app is overriding this method you can upgrade by removing the callback, making it async and returning the device list.

In SDK v2 you might have implemented an `onPairListDevices` method like this:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require('homey');

class Driver extends Homey.Driver {
  onPairListDevices(data, callback) {
    const discoveryStrategy = this.getDiscoveryStrategy();
    const discoveryResults = Object.values(
      discoveryStrategy.getDiscoveryResults()
    );

    const devices = discoveryResults.map((discoveryResult) => {
      return {
        name: discoveryResult.txt.name,
        data: {
          id: discoveryResult.id,
        },
      };
    });

    callback(null, devices);
  }
}

module.exports = Driver;
```

{% endcode %}

In SDK v3 this turns into:

{% code title="/drivers/\<driver\_id>/driver.js" %}

```javascript
const Homey = require('homey');

class Driver extends Homey.Driver {
  async onPairListDevices() {
    const discoveryStrategy = this.getDiscoveryStrategy();
    const discoveryResults = Object.values(
      discoveryStrategy.getDiscoveryResults()
    );

    const devices = discoveryResults.map((discoveryResult) => {
      return {
        name: discoveryResult.txt.name,
        data: {
          id: discoveryResult.id,
        },
      };
    });

    return devices;
  }
}

module.exports = Driver;
```

{% endcode %}

## App#onInit() called before Driver and Device onInit()

In SDK v2 your [`App#onInit()`](https://apps-sdk-v3.developer.homey.app/App.html#onInit) method would be executed after all managers where ready. Unfortunately this also meant that your Driver and Device `onInit` methods where executed before your [`App#onInit()`](https://apps-sdk-v3.developer.homey.app/App.html#onInit). This ordering was somewhat confusing so we changed the order in which we call the `onInit` methods in SDK v3.

In an app with an `app.js` and two drivers (`driver-one` and `driver-two` both have a single device) the order of `onInit` calls is now:

1. [`App#onInit()`](https://apps-sdk-v3.developer.homey.app/App.html#onInit)
2. [`Driver#onInit()`](https://apps-sdk-v3.developer.homey.app/Driver.html#onInit) (`driver-one`)
3. [`Device#onInit()`](https://apps-sdk-v3.developer.homey.app/Device.html#onInit)
4. [`Driver#onInit()`](https://apps-sdk-v3.developer.homey.app/Driver.html#onInit) (`driver-two`)
5. [`Device#onInit()`](https://apps-sdk-v3.developer.homey.app/Device.html#onInit)

The consequence of this change is that in your [`App#onInit()`](https://apps-sdk-v3.developer.homey.app/App.html#onInit) you cannot access Drivers (`this.homey.drivers.getDriver()` will throw an error). Instead of accessing Drivers from your App you can use [`App#onInit()`](https://apps-sdk-v3.developer.homey.app/App.html#onInit) to set up any data or classes that you might need in your application, then when your Driver or Device's `onInit` methods are called you are able to access this data directly through `this.homey.app`.

## Capabilities

The capabilities `alarm_contact` and `alarm_motion` activated a zone when an alarm is triggered. As of Homey v5.0.0 it is possible to disable this behaviour by using the capability option `zoneActivity`. This option accepts a boolean and defaults to `true`. This makes it possible to disable zone activity for these capabilities.

### Promises in App settings / Custom pair views

All methods in the Custom pair and App settings views now support callbacks and promises. The guides have been updated to show the usage with promises but callbacks remain supported for now.

{% hint style="info" %}
It is advised to update your code to use promises only for any API, because callbacks will be removed in a later SDK version.
{% endhint %}

## Z-Wave and MeshDriver

### Promises

Since all APIs are now promise-only, the way to interact with a Z-Wave node from within your app is promise-only as well. For example, previously you could execute a command like this:

{% code title="/drivers/\<driver\_id>/device.js" %}

```javascript
const { ZwaveDevice } = require('homey-meshdriver');

class Device extends ZwaveDevice {
  onMeshInit() {
    this.node.CommandClass.COMMAND_CLASS_BASIC.BASIC_SET(
      { Value: true },
      (err, result) => {
        // command has been executed
      }
    );
  }
}

module.exports = Device;
```

{% endcode %}

When using SDK version 3 this is no longer possible, and you should use promises only:

{% code title="/drivers/\<driver\_id>/device.js" %}

```javascript
const { ZwaveDevice } = require('homey-zwavedriver');

class Device extends ZwaveDevice {
  async onNodeInit() {
    await this.node.CommandClass.COMMAND_CLASS_BASIC.BASIC_SET({ Value: true });
    // command has been executed
  }
}

module.exports = Device;
```

{% endcode %}

### MeshDriver

Additionally, the [MeshDriver](https://github.com/athombv/node-homey-meshdriver) library is only compatible with SDK version 2. In order to create drivers for Z-Wave devices on Homey v5.0.0, a new (Z-Wave only) library has been made available for SDK version 3: [ZwaveDriver](https://github.com/athombv/node-homey-zwavedriver). Breaking changes are kept to a minimum to reduce the amount of effort to implement [ZwaveDriver](https://github.com/athombv/node-homey-zwavedriver) over MeshDriver. The most important changes can be found in the [documentation](https://athombv.github.io/node-homey-zwavedriver/).

### Change to associationGroups behaviour

The behaviour of the `zwave.associationGroups` driver property has changed in Homey v5.0.0 to be more predictable:

* `associationGroups: []` will now remove the default association group 1 (Z-Wave Plus lifeline)
* Not specifying `associationGroups` will now set the default association group 1 (Z-Wave Plus lifeline)

{% hint style="warning" %}
If you are updating your app you should make sure that drivers that either do not specify `associationGroups` or that set `associationGroups` to an empty array still behave correctly on Homey v5.0.0.
{% endhint %}

## Zigbee and MeshDriver

### Improved Zigbee stack

With Homey v5.0.0 comes a new and improved, built from scratch, Zigbee software stack. Zigbee apps developed for SDK version 2 will have to be updated to SDK version 3 in order to run on Homey v5.0.0 and higher.

### MeshDriver

Similar to Z-Wave, Zigbee will also come with a new (Zigbee-only) library to make developing Zigbee drivers a breeze: [ZigbeeDriver](https://github.com/athombv/node-homey-zigbeedriver). The breaking changes are kept to a minimum to reduce the amount of effort to implement [ZigbeeDriver](https://github.com/athombv/node-homey-zigbeedriver) over MeshDriver. Take a look at the [documentation](https://athombv.github.io/node-homey-zigbeedriver/) to find out about the most important changes.

Additionally, we created another library specifically for clusters: [ZigbeeClusters](https://github.com/athombv/node-zigbee-clusters). It is implemented by [ZigbeeDriver](https://github.com/athombv/node-homey-zigbeedriver) and is the place where all the Zigbee clusters are defined. In the case you need to add new Zigbee clusters, update existing clusters or are looking at more advanced features such as implementing custom clusters, take a look at the [documentation](https://athombv.github.io/node-zigbee-clusters/).

{% hint style="info" %}
We have updated the Zigbee guide and added a guide to upgrade from SDK version 2 to SDK version 3.
{% endhint %}

## App timezone now always UTC

In SDK v3 the default timezone (`process.env.TZ`) will always be set to `UTC`. In SDK v2, the timezone of an app would match the timezone the user set in their settings. This behaviour was confusing and could cause correct apps to have bugs when a user changes their timezone. To have more consistent and predictable behaviour all dates in SDK v3 apps will now default to `UTC`.

Some examples of the code that is affected by this change are:

* `new Date('3/14/20')`
* `Date.parse('3/14/20')`
* `myDate.getHours()`
* `myDate.setHours(12)`
* ...

Here's a full example how to retrieve the current Homey's timezone, and log a localized time string.

```javascript
const timezone = await this.homey.clock.getTimezone(); // e.g. Europe/Amsterdam
const formatter = new Intl.DateTimeFormat([], {
  timeZone: timezone,
  hour: '2-digit',
  minute: '2-digit',
  hour12: false, // Use 24-hour format
});

const timeParts = formatter.formatToParts(new Date());
const hour = timeParts.find(part => part.type === 'hour').value;
const minute = timeParts.find(part => part.type === 'minute').value;

this.log(`The time is ${hour}:${minute}`); // e.g. The time is 13:37
```

## ManagerCron removal

ManagerCron has been removed. We advice you to use `this.homey.setTimeout`, `this.homey.clearTimeout`, `this.homey.setInterval` and `this.homey.clearInterval` instead. These are the same as the native variants, but will take care of clearing the timeouts/intervals themselves when the app gets removed. Alternatively you could use `this.homey.on('unload', () => clearInterval(myInterval))`.

## Removed deprecated APIs

The previously deprecated `Image.format`, `Image.getFormat()`, `Image.getBuffer()` and `Image.setBuffer()` APIs have been removed. More information can be found in the [Image Api guide](https://apps.developer.homey.app/advanced/images).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apps.developer.homey.app/upgrade-guides/upgrading-to-sdk-v3.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
