SDK Changelog

Homey v5.0.0

Homey Apps SDK v3

This release of Homey 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 promises 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.json manifest from 2 to 3. Since SDK v3 is introduced in Homey 5.0.0 the Homey compatibility field in your app.json should be changed to >=5.0.0.

app.json

{
  "id": "com.athom.example",
  "sdk": 3,
  "compatibility": ">=5.0.0"
}

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.

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. homey-meshdriver has some additional changes that you should be aware of please continue reading this changelog for more information.

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, there is no need to create any resources and register them anymore, for example previously you would write the following code:

const Homey = require('homey');

class MyDriver 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);
  }
}

In SDK v3 this turns into:

const Homey = require('homey');

class MyDriver 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');
  }
}

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.

Creating and triggering Flow cards

Since most apps supports custom Flow cards here is an example of how to register and trigger Flow cards in SDK v3. Read the Flow for a more complete tutorial on how to use the Flow card API.

const Homey = require('homey');

class MyDriver 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);
  }
}

Web API improvements

You now need to define your API routes in app.json instead of having the definition and implementation both in api.js. Additionally since you can no longer get 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 tutorial. Here is an example of the new structure for Homey App Web API's:

/app.json

  ...
  "api": {
    "getSomething": {
      "method": "get",
      "path": "/"
    },
  },
  ...

/api.js

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

Consistent-ified APIs

We simplified some APIs with the goal of making them more constistent. This is to say we removed Driver.getManifest() and Device.getDriver() in favour of using properties: Driver#manifest and Device#driver.

Additionally, the signature of Device#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 to find the signature of all methods.

Async pairing socket

The argument that is passed to Driver.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:

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

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

In SDK v3 this turns into:

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

      return 'reply';
    });
  }
}

This also affects Driver.onPairListDevices(deviceData). 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:

module.exports = class MyDriver 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);
  }
};

In SDK v3 this turns into:

module.exports = class MyDriver 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;
  }
};

App#onInit() called before Driver and Device onInit

In SDK v2 your App#onInit() method would be executed after all managers where ready, unfortunately this also means that your Driver and Device onInit methods where executed before your App#onInit(). This ordering was somewhat suprising 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 which both have a single device) the order of onInit calls is now:

  1. App#onInit()
  2. Driver#onInit() (driver-one)
  3. Device#onInit()
  4. Driver#onInit() (driver-two)
  5. Device#onInit()

The consequence of this change is that in your App#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() 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.

Promises in App settings / Custom pair views

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

It is advised to update your code to use promises only for any API, because callbacks will be removed in a later SDK version.

Z-Wave and MeshDriver

Promises

Since all APIs are now promise-only, the way you 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:

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

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

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

MeshDriver

Additionally, the MeshDriver library is only compatible with SDK version 2. In order to easily create drivers for Z-Wave devices on Homey v5.0.0 a new (Z-Wave only) library has been made available specifically and only for SDK version 3: ZwaveDriver. The breaking changes are kept to a minimum to reduce the amount of effort to implement ZwaveDriver over MeshDriver. Take a look at the documentation to find out about the most important changes.

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 on SDK version 2 will have to be updated to SDK version 3 in order to run on Homey v5.0.0 and higher. This is a consequence of the new Zigbee stack.

MeshDriver

Similar to Z-Wave, Zigbee will also come with a new (Zigbee-only) library to make developing Zigbee drivers a breeze: ZigbeeDriver. It is also only compatible with SDK version 3. The breaking changes are kept to a minimum to reduce the amount of effort to implement ZigbeeDriver over MeshDriver. Take a look at the documentation to find out about the most important changes.

Additionally, another library that might be useful is ZigbeeClusters. It is implemented by 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.

We have updated the Zigbee documentation and added a guide to upgrade from SDK version 2 to SDK version 3: Upgrading from SDK v2.

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. We think this behaviour could be surprising and cause otherwise 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)
  • ...

Removed ManagerCron

ManagerCron has been removed. We advice developers to use this.homey.setTimeout, this.homey.clearTimeout, this.homey.setInterval and this.homey.clearInterval instead. These the same as the native variants but will take care of clearing the timeouts/intervals 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, check out the Image tutorial to learn more about the Image API.

Homey v3.1.0

Homey v3.1.0 introduces Maintenance Actions for devices. This feature is only available to users running the Homey app v3.0.1 or higher.

Homey v3.0.0

Homey v3.0.0 introduces Homey Energy. Developers should update their apps accordingly to be visible under the Energy tab in the Homey app.

Device now has new methods Device.addCapability(), Device.removeCapability(), Device.setCapabilityOptions(), Device.setClass(), Device.getEnergy() and Device.setEnergy().

Homey v2.5.0

Homey v2.5.0 introduces Discovery, an easy way to find devices on the local network.

Homey v2.2.0

Homey v2.2.0 introduces a new Image API that is optimized for faster, efficient and more frequent use. The buffer-based API's have been deprecated in favor of Image.setStream and Image.getStream. Image formats are inferred and no longer need to be supplied with the creation of the image. This allows for images with changing formats.

It is possible to build apps with dual-compatibility. Continue to supply the format parameter to the image constructor, then use if (image.setStream) to check if the new API is available for use, and use the Image.setBuffer API otherwise.

Homey v2.0.0

Since Homey v2.0.0, the desktop interface has been removed in favor of the Homey app. You should verify that your apps still looks great in the new interface.

Devices are now capability-based

Previously, a device's class was dominant in deciding what a device could do, such as which Flow cards were rendered or how the mobile card looked like.

In Homey v2.0, a device's capabilities are leading and the device's class is now just a hint for some interfaces (e.g. speech).

Practically, this means that:

  • A device's Flow cards are generated based on a device's system capabilities
  • A device's speech commands are based on a device's system capabilities

Virtual Classes

A device's virtual class can now be changed in the device's settings. The choose_slave pair template is deprecated and will be skipped automatically.

Devices with the windowcoverings and socket device class, the user can now select a virtual class.

Removal of the Device.mobile property

The mobile property for a device that previously defined how the mobile card should look has been removed. A device's mobile interface is now automatically generated based on the device's capabilities and class.

System capabilities are rendered correctly automatically. Custom capabilities will default to a component fitted to their type, getable and setable properties. The component choice can be overwritten by a new property uiComponent.

Addition of the Driver.deprecated and FlowCard.deprecated property

A Driver and Flow card now accepts "deprecated": true in their manifest. This will not show the said Driver of Flow card for new users, but will keep working for existing users.

Web API

Usage of the Web API has been limited in apps with the homey:manager:api permission to the following scopes: homey.alarm homey.system.readonly homey.updates.readonly homey.geolocation.readonly homey.zone.readonly homey.device.readonly homey.device.control homey.flow.readonly homey.user.readonly homey.user.self homey.app.readonly homey.app.control homey.presence.readonly homey.insights.readonly homey.logic homey.speech.

Users wishing to use the Web API in their app must include athom-api v2.1 or higher in their app.

Custom Capabilities

  • A custom capability can now have an icon property, which is an absolute path to a .svg file. This icon is rendered in some UI components.

Promise-only APIs

ManagerBluetooth now returns only promises and callbacks aren't accepted anymore.

It is advised to update your code to use promises only for any API, because callbacks will be removed in a later SDK version.

Insights

Insights now work on a round-robin database style, which means that individual entries won't be logged but aggregated over time.

Practically this means that all your app's numeric Logs will have a minimum interval of 5 seconds.

Music

Homey Music has been removed in favor of the media component and the speaker_ capabilities.

Webviews

Webviews are now mobile-only. Libraries that Homey previously provided such as jQuery and Angular are deprecated and developers should include their own libraries.

Deprecation List

  • HD0001 - new Image(format), Image.format, Image.getFormat(), Image.getBuffer() and Image.setBuffer() are deprecated, please use Image.pipe() Image.setStream and Image.getStream