Skip to content
Get the appGet app

Hosting Widget Pictures

A Switchboard tile can show a picture from its image.url. Aivi never downloads that picture: each phone showing the widget fetches it on its own, without your credentials. So the picture has to live somewhere every phone can reach, from anywhere, over https://. This page shows how to prepare a picture and two ways to host it: a free Cloudflare R2 bucket, which we recommend, and the Home Assistant www folder.

The full rules are under Pictures; in short:

  • Public https://, reachable from anywhere. A phone away from home, or a family member’s phone, fetches the same URL. A LAN address such as http://192.168.1.20/... or homeassistant.local won’t load there.
  • Stable. The URL must keep working. Links that expire, such as signed storage URLs or tokenized camera snapshots, stop loading after a while.
  • Small. At most 512 pixels on a side and 256 KB, in PNG, JPEG, HEIC, WebP, or GIF. Anything larger is refused, and a URL that fails isn’t tried again for 30 minutes.
  • A new URL for a new picture. The phone trusts a picture for 24 hours. To change it sooner, change the URL, for example dog-cam.jpg?v=2 or dog-cam-2.jpg.
  • Short. Image URLs count toward the 4 KiB content limit, so a short domain and file name leave more room for your tiles.

Anyone who knows the URL can open the picture, so only host pictures you don’t mind being public.

Most photos are far larger than 512 pixels. On a Mac, sips resizes a photo so its longer side is 512 pixels, and saves it as a JPEG:

sips -Z 512 -s format jpeg -s formatOptions 80 photo.jpg --out dog-cam.jpg

It also reads iPhone .heic photos. With ImageMagick on any platform, the same is:

magick photo.jpg -resize '512x512>' -quality 80 dog-cam.jpg

A 512-pixel JPEG at this quality is usually well under 100 KB. Check the size before you upload it; if it’s over 256 KB, lower the quality.

Cloudflare R2 is object storage with a free monthly allowance: 10 GB of storage, 1 million write operations (such as uploads), and 10 million read operations. Downloads (egress) are free. A handful of thumbnails fetched by a few phones stays far inside it.

  1. Sign in to the Cloudflare dashboard, or create a free account, and open Storage & databases > R2 > Overview. The first time, complete the checkout to add R2 to your account. It includes the free monthly allowance, and you’re billed only for usage beyond it.

  2. Select Create bucket, give it a name such as aivi-pictures (lowercase letters, numbers, and hyphens), and select Create bucket. The default location and storage class are fine.

  3. Make the bucket public. In the bucket, open Settings. Under Public Development URL, select Enable, type allow to confirm, and select Allow. The bucket’s public URL, on r2.dev, appears there.

    If you have a domain on Cloudflare, connect it instead: under Custom Domains, select Add, enter a subdomain such as pictures.example.com, select Continue, and then Connect Domain. See Custom domain or r2.dev.

  4. Back in the bucket, select Upload and drag in your prepared picture.

  5. Put the public URL and the file name together, and use it as the tile’s image.url:

    "image": {
    "url": "https://pictures.example.com/dog-cam.jpg",
    "alt": "The dog in the hallway"
    }

    Open the URL in a private browser window first: if the picture shows there, the phone can load it too.

To replace a picture, upload the new file under a new name, or keep the name and change the version in your payload (dog-cam.jpg?v=2). Either way, the phone loads it at the next sync.

Cloudflare describes the r2.dev URL as meant for development: it is rate-limited, and Cloudflare recommends a custom domain for production. A personal widget is a light load: each phone fetches a picture about once a day, so r2.dev is fine for a home setup. If you already have a domain on Cloudflare, connect it anyway: the URL is shorter, it’s yours, and Cloudflare can cache the pictures. The domain must be a zone in the same Cloudflare account as the bucket.

Home Assistant serves the files in its www folder at /local/, without asking for a login. That makes it a quick place for a few pictures, but only if your Home Assistant is reachable from the internet over https://: with Home Assistant Cloud remote access, or your own setup such as a reverse proxy with a certificate. If your Home Assistant is only reachable at home (or only over a VPN), the picture won’t load on a phone away from it; use Cloudflare R2 instead.

  1. Create a folder named www inside your configuration folder (/config), for example with the File editor or Samba add-on. If the folder is new, restart Home Assistant once so it starts serving it.

  2. Put your prepared picture in it, for example /config/www/aivi/dog-cam.jpg.

  3. Build the URL from your public Home Assistant address and the path below www, with /local/ in front:

    "image": {
    "url": "https://ha.example.com/local/aivi/dog-cam.jpg?v=1",
    "alt": "The dog in the hallway"
    }

    Open it in a private browser window on mobile data to check that it loads away from home.

Anyone who knows a /local/ URL can open that file, so keep only pictures you would share in www. When you replace a file, bump the version (?v=2) in the payload so phones fetch the new one.

Any static host works if the URL is public https:// and stays the same: a web server you already run, another object storage service with public reads, or your own website. Avoid links that expire: signed or presigned storage URLs, camera snapshot links with a token, and share links that need a login all stop working, and the tile falls back to its gradient or symbol.