# Images

When an `Image` is created, it needs a way of providing Homey with its data. This can be either:

* an URL, available from anywhere on the internet
* a binary stream through the `getStream` method
* a local path to a static image which is shipped with the App.

{% hint style="warning" %}
Note: Images are limited to 5 MB.
{% endhint %}

{% hint style="info" %}
You can debug your images in the [Developer Tools](https://tools.developer.homey.app/tools/images).
{% endhint %}

## Creating an image

### Using an URL

URLs should be used when the image is available publicly on the internet.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/app.js" %}

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

class App extends Homey.App {
  async onInit() {
    const myImage = await this.homey.images.createImage();
    // the URL must start with https://
    myImage.setUrl("https://www.example.com/image.png");
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/app.mts" %}

```mts
import Homey from "homey";

export default class App extends Homey.App {
  async onInit(): Promise<void> {
    const myImage = await this.homey.images.createImage();
    // the URL must start with https://
    myImage.setUrl("https://www.example.com/image.png");
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/app.py" %}

```python
from homey import app


class App(app.App):
    async def on_init(self) -> None:
        my_image = await self.homey.images.create_image()
        # the URL must start with https://
        my_image.set_url("https://www.example.com/image.png")


homey_export = App

```

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

### Using a Stream

{% tabs %}
{% tab title="JavaScript" %}
Streams should be used when downloading an image that cannot be supplied using [`Image#setUrl()`](https://apps-sdk-v3.developer.homey.app/Image.html#setUrl). Using image streams involves writing data directly into a Node.js stream. Using streams requires homey version 2.2.0 or higher.

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

```javascript
const Homey = require('homey');
const fetch = require("node-fetch");

class App extends Homey.App {
  async onInit() {
    const myImage = await this.homey.images.createImage();

    myImage.setStream(async (stream) => {
      const res = await fetch("http://192.168.1.100/image.png");
      if (!res.ok) {
        throw new Error("Invalid Response");
      }

      return res.body.pipe(stream);
    });
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
Streams should be used when downloading an image that cannot be supplied using [`Image#setUrl()`](https://apps-sdk-v3.developer.homey.app/Image.html#setUrl). Using image streams involves writing data directly into a Node.js stream. Using streams requires homey version 2.2.0 or higher.

{% code title="/app.mts" %}

```mts
import Homey from "homey";
import fetch from "node-fetch";
import type { Writable } from "stream";

export default class App extends Homey.App {
  async onInit(): Promise<void> {
    const myImage = await this.homey.images.createImage();

    myImage.setStream(async (stream: Writable) => {
      const res = await fetch("http://192.168.1.100/image.png");
      if (!res.ok || res.body === null) {
        throw new Error("Invalid Response");
      }

      return res.body.pipe(stream);
    });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
Streams should be used when downloading an image that cannot be supplied using [`Image#set_url()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.set_url). Using image streams involves writing data directly into a io.BytesIO stream. Using streams requires homey version 2.2.0 or higher.

{% code title="/app.py" %}

```python
from io import BytesIO

import aiohttp
from homey import app


class App(app.App):
    async def on_init(self) -> None:
        my_image = await self.homey.images.create_image()

        async def stream_image(stream: BytesIO):
            async with aiohttp.ClientSession() as session:
                async with session.get("http://192.168.1.100/image.png") as res:
                    if not res.ok:
                        raise Exception("Invalid Response")
                    stream.write(await res.read())

        my_image.set_stream(stream_image)


homey_export = App

```

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

### Using a Path

Paths should be used when the image is locally available.

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/app.js" %}

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

class App extends Homey.App {
  async onInit() {
    const myImage = await this.homey.images.createImage();
    myImage.setPath("/userdata/image.png");
  }
}

module.exports = App;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/app.mts" %}

```mts
import Homey from "homey";

export default class App extends Homey.App {
  async onInit(): Promise<void> {
    const myImage = await this.homey.images.createImage();
    myImage.setPath("/userdata/image.png");
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/app.py" %}

```python
from homey import app


class App(app.App):
    async def on_init(self) -> None:
        my_image = await self.homey.images.create_image()
        my_image.set_path("/userdata/image.png")


homey_export = App

```

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

## Updating the image

{% tabs %}
{% tab title="JavaScript" %}
Call [`Image#update()`](https://apps-sdk-v3.developer.homey.app/Image.html#update) when the image has been updated, and the front-end will download the image again.

When your image uses a Stream, the method provided in [`Image#setStream()`](https://apps-sdk-v3.developer.homey.app/Image.html#setStream) will be called again.

At any time, you can switch between delivery type by calling [`Image#setPath()`](https://apps-sdk-v3.developer.homey.app/Image.html#setPath), [`Image#setStream()`](https://apps-sdk-v3.developer.homey.app/Image.html#setStream) or [`Image#setURL()`](https://apps-sdk-v3.developer.homey.app/Image.html#setURL).
{% endtab %}

{% tab title="TypeScript" %}
Call [`Image#update()`](https://apps-sdk-v3.developer.homey.app/Image.html#update) when the image has been updated, and the front-end will download the image again.

When your image uses a Stream, the method provided in [`Image#setStream()`](https://apps-sdk-v3.developer.homey.app/Image.html#setStream) will be called again.

At any time, you can switch between delivery type by calling [`Image#setPath()`](https://apps-sdk-v3.developer.homey.app/Image.html#setPath), [`Image#setStream()`](https://apps-sdk-v3.developer.homey.app/Image.html#setStream) or [`Image#setURL()`](https://apps-sdk-v3.developer.homey.app/Image.html#setURL).
{% endtab %}

{% tab title="Python" %}
Call [`Image#update()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.update) when the image has been updated, and the front-end will download the image again.

When your image uses a Stream, the method provided in [`Image#set_stream()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.set_stream) will be called again.

At any time, you can switch between delivery type by calling [`Image#set_path()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.set_path), [`Image#set_stream()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.set_stream) or [`Image#set_url()`](https://python-apps-sdk-v3.developer.homey.app/image.html#homey.image.Image.set_url).
{% endtab %}
{% endtabs %}

## Retrieving an image

It is also possible to consume an image in your app, for instance through use of Flow Tokens.

```javascript
const { PassThrough } = require("stream");
const fetch = require("node-fetch");
const FormData = require("form-data");

//uploads an image to imgur and returns a link
async function uploadImage(image) {
  const stream = await image.getStream();

  const form = new FormData();

  form.append("image", stream, {
    contentType: stream.contentType,
    filename: stream.filename,
    name: "image",
  });

  form.append(
    "description",
    `This image can also be (temporarily) viewed at: ${image.cloudUrl} and ${image.localUrl}`
  );

  const response = await fetch("https://api.imgur.com/3/image", {
    method: "POST",
    //pipe through a passthrough stream, workarround for a node-fetch bug involving form-data streams without content length set.
    body: form.pipe(new PassThrough()),
    headers: {
      ...form.getHeaders(),
      Authorization: "Client-ID <YOUR_CLIENT_ID>",
    },
  });

  if (!response.ok) {
    throw new Error(response.statusText);
  }

  const { data } = await response.json();
  return data.link;
}
```
