# Drivers & Devices

{% embed url="<https://www.youtube.com/watch?v=1M1aNHqFBdc>" %}

For every device paired with Homey, a `Device` class will be created and a device tile will be shown in the interface. Every `Device` class is associated with a `Driver` class. All `Driver` classes of your app will be instantiated when your app is started, even if there are no devices of that type paired with Homey. This allows the driver to be responsible for pairing new devices and defining Flow cards.

![](https://998911913-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MPk9cn4V7WnnKt7fbry%2Fuploads%2Fgit-blob-d294afc945f0a335a22fde043d5eed84ce4c7649%2Fdiagram-devices.png?alt=media)

You can use the Homey CLI to interactively create a driver, this will create a basic driver manifest and all the required files for your driver:

```bash
homey app driver create
```

This command will ask a number of questions about the driver you want to create, and will then create a new folder in your `drivers/` directory which will look like this:

{% tabs %}
{% tab title="JavaScript" %}

```
com.athom.example/driver/<driver_id>/
├─ assets/
│ └─ ...
├─ device.js
├─ driver.js
└─ driver.compose.json
```

The `/drivers/<driver_id>/driver.js` file contains the `Driver` class. This class is responsible for pairing devices and describes the functionality for the devices belonging to the driver such as Flow cards.

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

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

class Driver extends Homey.Driver {
  // this method is called when the app is started and the Driver is inited
  async onInit() {
    const showToastActionCard = this.homey.flow.getActionCard('show_toast');
  
    showToastActionCard.registerRunListener(async ({ device, message }) => {
      await device.createToast(message);
    });
  }
  
  // This method is called when a user is adding a device
  // and the 'list_devices' view is called
  async onPairListDevices() {
    return [
      {
        name: "Foo Device",
        data: {
          id: "abcd1234",
        },
      },
    ];
  }
}

module.exports = Driver;
```

{% endcode %}

{% hint style="info" %}
The shown `onPairListDevices` is a heavily simplified example. Learn more about pairing in the [Pairing documentation](https://apps.developer.homey.app/the-basics/devices/pairing).
{% endhint %}

The `/drivers/<driver_id>/device.js` file contains the `Device` class. This class implements the device's functionality such as its capabilities and Flows.

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

```javascript
const Homey = require("homey");
const DeviceApi = require("device-api");

class Device extends Homey.Device {
  // this method is called when the Device is inited
  async onInit() {
    this.log("Device init");
    this.log("Name:", this.getName());
    this.log("Class:", this.getClass());

    // register a capability listener
    this.registerCapabilityListener("onoff", this.onCapabilityOnoff.bind(this));
  }

  // this method is called when the Device has requested a state change (turned on or off)
  async onCapabilityOnoff(value, opts) {
    // ... set value to real device, e.g.
    // await setMyDeviceState({ on: value });
    // or, throw an error
    // throw new Error('Switching the device failed!');
  }
  
  // this is a custom method for the 'show_toast' Action Flow card as
  // shown in the Driver example above
  async createToast(message) {
    await DeviceApi.createToast(message);
  }
}

module.exports = Device;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}

```
com.athom.example/driver/<driver_id>/
├─ assets/
│ └─ ...
├─ device.mts
├─ driver.mts
└─ driver.compose.json
```

The `/drivers/<driver_id>/driver.mts` file contains the `Driver` class. This class is responsible for pairing devices and describes the functionality for the devices belonging to the driver such as Flow cards.

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

```mts
import Homey from 'homey';
import type Device from './device.mjs';

export default class Driver extends Homey.Driver {
  // this method is called when the app is started and the Driver is initialized
  async onInit(): Promise<void> {
    const showToastActionCard = this.homey.flow.getActionCard('show_toast');

    showToastActionCard.registerRunListener(async ({ device, message }: { device: Device; message: string }) => {
      await device.createToast(message);
    });
  }

  // This method is called when a user is adding a device
  // and the 'list_devices' view is called
  async onPairListDevices(): Promise<object[]> {
    return [
      {
        name: 'Foo Device',
        data: {
          id: 'abcd1234',
        },
      },
    ];
  }
}

```

{% endcode %}

{% hint style="info" %}
The shown `onPairListDevices` is a heavily simplified example. Learn more about pairing in the [Pairing documentation](https://apps.developer.homey.app/the-basics/devices/pairing).
{% endhint %}

The `/drivers/<driver_id>/device.mts` file contains the `Device` class. This class implements the device's functionality such as its capabilities and Flows.

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

```mts
import Homey from 'homey';
import DeviceApi from 'device-api';

export default class Device extends Homey.Device {
  // this method is called when the Device is initialized
  async onInit(): Promise<void> {
    this.log('Device init');
    this.log('Name:', this.getName());
    this.log('Class:', this.getClass());

    // register a capability listener
    this.registerCapabilityListener('onoff', this.onCapabilityOnoff.bind(this));
  }

  // this method is called when the Device has requested a state change (turned on or off)
  async onCapabilityOnoff(value: boolean, opts: Record<string, unknown>): Promise<void> {
    // ... set value to real device, e.g.
    // await setMyDeviceState({ on: value });
    // or, throw an error
    // throw new Error('Switching the device failed!');
  }

  // this is a custom method for the 'show_toast' Action Flow card as
  // shown in the Driver example above
  async createToast(message: string): Promise<void> {
    await DeviceApi.createToast(message);
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}

```
com.athom.example/driver/<driver_id>/
├─ assets/
│ └─ ...
├─ device.py
├─ driver.py
└─ driver.compose.json
```

The `/drivers/<driver_id>/driver.py` file contains the `Driver` class. This class is responsible for pairing devices and describes the functionality for the devices belonging to the driver such as Flow cards.

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

```python
from homey import driver
from homey.driver import ListDeviceProperties

from .device import Device


class Driver(driver.Driver):
    async def on_init(self) -> None:
        show_toast_action_card = self.homey.flow.get_action_card("show_toast")

        async def on_show_toast(card_arguments, **trigger_kwargs) -> None:
            device: Device = card_arguments["device"]
            message: str = card_arguments["message"]
            await device.create_toast(message)

        show_toast_action_card.register_run_listener(on_show_toast)

    # This method is called when a user is adding a device
    # and the 'list_devices' view is called
    async def on_pair_list_devices(self, view_data: dict) -> list[ListDeviceProperties]:
        return [{"name": "Foo Device", "data": {"id": "abcd1234"}}]


homey_export = Driver

```

{% endcode %}

{% hint style="info" %}
The shown `on_pair_list_devices` is a heavily simplified example. Learn more about pairing in the [Pairing documentation](https://apps.developer.homey.app/the-basics/devices/pairing).
{% endhint %}

The `/drivers/<driver_id>/device.py` file contains the `Device` class. This class implements the device's functionality such as its capabilities and Flows.

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

```python
from device_api import DeviceApi
from homey import device


class Device(device.Device):
    # this method is called when the Device is initialized
    async def on_init(self) -> None:
        self.log("Device init")
        self.log("Name:", self.get_name())
        self.log("Class:", self.get_class())

        # register a capability listener
        self.register_capability_listener("onoff", self.on_capability_onoff)

    async def on_capability_onoff(self, value: bool, **opts) -> None:
        # ... set the value to a real device, e.g.
        # await set_my_device_state(on=value)
        # or, throw an error
        # raise Exception("Switching the device failed!")
        pass

    # this is a custom method for the 'show_toast' Action Flow card as
    # shown in the Driver example above
    async def create_toast(self, message: str) -> None:
        await DeviceApi.create_toast(message)


homey_export = Device

```

{% endcode %}
{% endtab %}
{% endtabs %}

## Driver Manifest

The `/drivers/<driver_id>/driver.compose.json` file is the driver manifest, when building your app all the `driver.compose.json` files will be bundled into your App Manifest. A basic driver manifest looks like this:

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

```javascript
{
  "name": { "en": "My Driver" },
  "class": "socket",
  "capabilities": ["onoff", "dim"],
  "images": {
    "small": "/drivers/my_driver/assets/images/small.png",
    "large": "/drivers/my_driver/assets/images/large.png",
    "xlarge": "/drivers/my_driver/assets/images/xlarge.png"
  },
  "platforms": ["local", "cloud"],
  "connectivity": ["lan"],
  "pair": [
    {
      "id": "list_devices",
      "template": "list_devices",
      "navigation": { "next": "add_devices" }
    },
    {
      "id": "add_devices",
      "template": "add_devices"
    }
  ]
}
```

{% endcode %}

### Icon

The driver icon location cannot be specified in the driver manifest, instead the driver's icon is always expected to be located at `/drivers/<driver_id>/assets/icon.svg`. Read the [app store guidelines](https://apps.developer.homey.app/app-store/guidelines#1.5.-icons) for more information about the driver icon.

### Platforms

`"platforms": ["local", "cloud"]`

An array containing the driver's supported platforms. Read the [Homey Cloud](https://apps.developer.homey.app/guides/homey-cloud) guide for more information.

### Device Class

`"class": "light"`

The device class tells Homey what type of device your driver adds support for. Examples of device classes are `socket`, `light`, `lock` etc. When a specific device is not supported by Homey, you can use the class `other`.

Device classes play a crucial role in enhancing the user experience within the Homey platform. By properly configuring classes for your devices, you ensure seamless integration with various features and services.

For instance, if a user utilizes a Zone Flow card to turn off all lights in a specific zone, your device will be automatically included if it has both the `onoff` capability and the `light` class. This ensures that the device is correctly recognized and controlled as a light.

Additionally, when a user has linked their Homey setup to Google Assistant, commands like "Turn off all lights" will function as expected. This is because you've assigned the appropriate class to your device, allowing it to be correctly identified and managed by voice assistants.

By carefully setting device classes, you help create a smooth and intuitive experience for users, both within the Homey ecosystem and with integrated third-party services like Google Assistant.

Find your device's class in the [Device Class Reference](https://apps-sdk-v3.developer.homey.app/tutorial-device-classes.html).

### Capabilities

`"capabilities": ["onoff", "dim"]`

The capabilities of a device describe the states and actions a device supports. For example, a light may have the capability `onoff` which allows users to toggle the light on or off. It can also support the `dim` capability which would allow the user to change the lights brightness.

Capabilities all have a data type, for example the `onoff` capability has the type `boolean` and can thus be either `true` or `false`. The capability `dim` is of type `number` and can be any number between `0 - 1`, as defined in the capability's definition.

Homey ships with many system capabilities. For other cases, app-specific capabilities can be defined in the [App Manifest](https://apps.developer.homey.app/the-basics/app/manifest).

Homey has built-in [Flow](https://apps.developer.homey.app/the-basics/flow) cards for every system capability.

Read more about [Capabilities](https://apps.developer.homey.app/the-basics/devices/capabilities).

### Energy

`"energy": {...}`

A device can use or generate power. To keep track of the data related to power usage and generation a device can have an energy object. This object contains power usage approximation data and flags to indicate a device generates power or should be omitted from automatic shutdown.

Read more about [Energy](https://apps.developer.homey.app/the-basics/devices/energy).

### Settings

`"settings": [ ... ]`

A device can have user-configurable settings, such as the orientation of a curtain or a poll-interval. Settings are shown to the user in the front-end, or can be changed programmatically (see [`Device#setSettings`](https://apps-sdk-v3.developer.homey.app/Device.html#setSettings)).

Read more about [Settings](https://apps.developer.homey.app/the-basics/devices/settings).

### Pairing

`"pair": [ ... ]`

A device can be added to Homey through pairing. Pairing is started when the user selects the device they want to add from the Homey app. The `pair` property of the device manifest describes the steps necessary to add the device to Homey.

For most devices a few simple pair steps are enough. For more advanced devices, where more user-steps are required, custom pairing views can be provided.

Read more about [Pairing](https://apps.developer.homey.app/the-basics/devices/pairing).

### Deprecated

`"deprecated": true`

Sometimes a Driver that has been available in the past should be removed. To not break compatibility for users that were using it, add `"deprecated": true` to your driver in your App Manifest. It will still work, but won't show up anymore in the 'Add Device' list.

### Connectivity

`"connectivity": [ ... ]`

Specify how the Driver connects to your device in the real world. You can specify multiple values, for example `[ "infrared", "lan" ]` for a TV that is turned on by Infrared, and then controlled over Wi-Fi LAN.

#### Allowed Values

| Value      | Description                                       |
| ---------- | ------------------------------------------------- |
| `lan`      | Local (Wi-Fi/Ethernet)                            |
| `cloud`    | Cloud-connected (Wi-Fi/Ethernet)                  |
| `ble`      | Bluetooth Low Energy                              |
| `zwave`    | Z-Wave                                            |
| `zigbee`   | Zigbee                                            |
| `infrared` | Infrared                                          |
| `rf433`    | 433 MHz                                           |
| `rf868`    | 868 MHz                                           |
| `matter`   | Matter (Only available on Homey Pro (Early 2023)) |

## Device identifier

During pairing, you must provide a `data` property. This property contains a unique identifier for the device. This object cannot be changed after pairing. This `data` property is an object containing any properties of the types String, Number or Object. Homey uses this object to identify your device, together with the driver's ID. Read more in the [Device pairing documentation](https://apps.developer.homey.app/the-basics/devices/pairing).

{% hint style="warning" %}
Only put the essential properties needed to identify a device in the data object. For example, a MAC address is a good property, an IP address is not, because it can change over time.
{% endhint %}

Any properties that could change over time should be kept in-memory or saved in the device's store.

## Availability

{% tabs %}
{% tab title="JavaScript" %}
A device can be marked as unavailable using [`Device#setUnavailable()`](https://apps-sdk-v3.developer.homey.app/Device.html#setUnavailable). This shows to the user that they cannot interacting with the device for example because the device is offline.

When a device is marked as unavailable, all capabilities and Flow actions will be prevented. When a device is available again, use [`Device#setAvailable()`](https://apps-sdk-v3.developer.homey.app/Device.html#setAvailable) to mark the device as available.

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

```javascript
const Homey = require("homey");
const DeviceApi = require("device-api");

class Device extends Homey.Device {
  async onInit() {
    await this.setUnavailable();

    DeviceApi.on("connected", (address) => {
      this.setAvailable().catch(this.error);
    });
    
    DeviceApi.on("disconnected", (address) => {
      this.setUnavailable().catch(this.error);
    });    
  }
}

module.exports = Device;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
A device can be marked as unavailable using [`Device#setUnavailable()`](https://apps-sdk-v3.developer.homey.app/Device.html#setUnavailable). This shows to the user that they cannot interacting with the device for example because the device is offline.

When a device is marked as unavailable, all capabilities and Flow actions will be prevented. When a device is available again, use [`Device#setAvailable()`](https://apps-sdk-v3.developer.homey.app/Device.html#setAvailable) to mark the device as available.

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

```mts
import Homey from 'homey';
import DeviceApi from 'device-api';

export default class Device extends Homey.Device {
  async onInit(): Promise<void> {
    await this.setUnavailable();

    DeviceApi.on('connected', (address: string) => {
      this.setAvailable().catch(this.error);
    });

    DeviceApi.on('disconnected', (address: string) => {
      this.setUnavailable().catch(this.error);
    });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
A device can be marked as unavailable using [`Device#set_unavailable()`](https://python-apps-sdk-v3.developer.homey.app/device.html#homey.device.Device.set_unavailable). This shows to the user that they cannot interacting with the device for example because the device is offline.

When a device is marked as unavailable, all capabilities and Flow actions will be prevented. When a device is available again, use [`Device#set_available()`](https://python-apps-sdk-v3.developer.homey.app/device.html#homey.device.Device.set_available) to mark the device as available.

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

```python
from device_api import DeviceApi
from homey import device


class Device(device.Device):
    async def on_init(self) -> None:
        await self.set_unavailable()

        async def on_connected(address: str) -> None:
            try:
                await self.set_available()
            except Exception as e:
                self.error(e)

        async def on_disconnected(address: str) -> None:
            try:
                await self.set_unavailable()
            except Exception as e:
                self.error(e)

        DeviceApi.on("connected", on_connected)
        DeviceApi.on("disconnected", on_disconnected)


homey_export = Device

```

{% endcode %}
{% endtab %}
{% endtabs %}

## Assets

Your drivers `/assets/` folder contains the icon and images for that driver. These images should be clean marketing pictures of the device that this driver implements, these are shown in the Homey App Store.

Read more about driver images and icons in the [Homey App Store guidelines](https://apps.developer.homey.app/app-store/guidelines#1-4-images).

## Store

In some cases you may want to store some information about the device that persists across reboots. For example you may need to store the device's IP address. The device's store is a persistent storage to save device properties. This store can be set during pairing and programmatically read and updated after the device is paired with Homey.

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

```javascript
const Homey = require("homey");
const DeviceApi = require("device-api");

class Device extends Homey.Device {
  async onInit() {
    this.currentAddress = this.getStoreValue("address");

    DeviceApi.on("address-changed", (address) => {
      this.currentAddress = address;
      this.setStoreValue("address", address).catch(this.error);
    });
  }
}

module.exports = Device;
```

{% endcode %}
{% endtab %}

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

```mts
import Homey from 'homey';
import DeviceApi from 'device-api';

export default class Device extends Homey.Device {
  currentAddress?: string;

  async onInit(): Promise<void> {
    this.currentAddress = this.getStoreValue('address');

    DeviceApi.on('address-changed', (address: string) => {
      this.currentAddress = address;
      this.setStoreValue('address', address).catch(this.error);
    });
  }
}

```

{% endcode %}
{% endtab %}

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

```python
from device_api import DeviceApi
from homey import device


class Device(device.Device):
    current_address: str | None

    async def on_init(self) -> None:
        self.current_address = self.get_store().get("address")

        async def on_address_changed(address: str) -> None:
            self.current_address = address
            try:
                await self.set_store_value("address", address)
            except Exception as e:
                self.error(e)

        DeviceApi.on("address-changed", on_address_changed)


homey_export = Device

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Using the store is pretty rare, usually there are other solutions.\
For example if you want users to be able to update these values easily you should use device settings instead. See the [device settings documentation](https://apps.developer.homey.app/the-basics/devices/settings) for more information.\
And instead of storing the devices IP address in the device store you could use the [local network device discovery](https://apps.developer.homey.app/wireless/wi-fi/discovery) functionality that is built into Homey.
{% endhint %}
