Integrations

Scriptable Widget

Display live EasyBoard tile values on your iPhone home screen using Scriptable — a free iOS app that runs JavaScript as a home screen widget. Tap the widget to open your full dashboard.

How updates work

iOS controls when widgets refresh in the background — typically every 15–30 minutes, though it can be longer depending on battery and usage patterns. This is a system limitation, not an EasyBoard one. You can always tap the widget to open the live dashboard in your browser for true real-time data via SSE.

What you'll need

  • Scriptable — free on the App Store (search "Scriptable" by Simon Støvring)
  • Your Dashboard ID — the string in the URL of your dashboard: easyboard.live/d/YOUR_DASHBOARD_ID

Setup

  1. 1

    Add the script to Scriptable

    Open Scriptable on your iPhone and tap the + button to create a new script. Name it EasyBoard. Copy the entire script below and paste it into the editor.

    EasyBoard.js
    // -- Configuration -------------------------------------------
    const DASHBOARD_ID = "PASTE_YOUR_DASHBOARD_ID_HERE";
    
    // Optional: filter by alternateId (set in EasyBoard tile settings).
    // Leave as [] to automatically show the first tile(s) on your dashboard.
    const TILE_IDS = []; // e.g. ["temperature", "humidity"]
    // -- End Configuration ----------------------------------------
    
    const BASE = "https://easyboard.live";
    
    async function fetchTiles() {
      const url = BASE + "/api/d/" + DASHBOARD_ID + "/tiles";
      const req = new Request(url);
      const json = await req.loadJSON();
      if (json && json.error) throw new Error("API error: " + json.error);
      return Array.isArray(json) ? json : [];
    }
    
    function displayValue(tile) {
      if (!tile) return "---";
      var unit = tile.unit || "";
      return unit ? tile.value + " " + unit : (tile.value || "---");
    }
    
    function selectTiles(allTiles) {
      if (TILE_IDS.length === 0) return allTiles.slice(0, 2);
      return TILE_IDS.map(function(id) {
        return allTiles.find(function(t) { return t.alternateId === id; });
      });
    }
    
    async function buildWidget() {
      var w = new ListWidget();
      w.backgroundColor = new Color("#0f172a");
      w.url = BASE + "/d/" + DASHBOARD_ID;
      w.refreshAfterDate = new Date(Date.now() + 15 * 60 * 1000);
    
      var allTiles = [];
      try {
        allTiles = await fetchTiles();
      } catch (e) {
        w.addSpacer();
        var errEl = w.addText(e.message || String(e));
        errEl.textColor = Color.red();
        errEl.font = Font.systemFont(11);
        errEl.minimumScaleFactor = 0.4;
        w.addSpacer();
        return w;
      }
    
      if (allTiles.length === 0) {
        w.addSpacer();
        var emptyEl = w.addText("No tiles on this dashboard yet");
        emptyEl.textColor = new Color("#94a3b8");
        emptyEl.font = Font.systemFont(11);
        emptyEl.minimumScaleFactor = 0.5;
        w.addSpacer();
        return w;
      }
    
      var tiles = selectTiles(allTiles);
      var isSmall = config.widgetFamily !== "medium" && config.widgetFamily !== "large";
    
      if (isSmall || tiles.length === 1) {
        // Small: one tile, big number
        var tile = tiles[0];
        w.addSpacer();
        var titleEl = w.addText(tile ? tile.title : "---");
        titleEl.font = Font.mediumSystemFont(11);
        titleEl.textColor = new Color("#94a3b8");
        titleEl.minimumScaleFactor = 0.7;
        w.addSpacer(6);
        var valueEl = w.addText(displayValue(tile));
        valueEl.font = Font.boldSystemFont(38);
        valueEl.textColor = Color.white();
        valueEl.minimumScaleFactor = 0.3;
        w.addSpacer();
        var dateEl = w.addDate(new Date());
        dateEl.font = Font.systemFont(9);
        dateEl.textColor = new Color("#475569");
        dateEl.applyRelativeStyle();
      } else {
        // Medium: two tiles side by side
        w.addSpacer();
        var row = w.addStack();
        row.layoutHorizontally();
        var count = Math.min(tiles.length, 2);
        for (var i = 0; i < count; i++) {
          var t = tiles[i];
          var col = row.addStack();
          col.layoutVertically();
          var tEl = col.addText(t ? t.title : "---");
          tEl.font = Font.mediumSystemFont(11);
          tEl.textColor = new Color("#94a3b8");
          tEl.minimumScaleFactor = 0.7;
          col.addSpacer(4);
          var vEl = col.addText(displayValue(t));
          vEl.font = Font.boldSystemFont(30);
          vEl.textColor = Color.white();
          vEl.minimumScaleFactor = 0.3;
          if (i === 0) row.addSpacer();
        }
        w.addSpacer(8);
        var dEl = w.addDate(new Date());
        dEl.font = Font.systemFont(9);
        dEl.textColor = new Color("#475569");
        dEl.applyRelativeStyle();
      }
    
      return w;
    }
    
    var widget = await buildWidget();
    if (config.runsInWidget) {
      Script.setWidget(widget);
    } else {
      await widget.presentSmall();
    }
    Script.complete();
  2. 2

    Set your Dashboard ID

    At the top of the script, replace PASTE_YOUR_DASHBOARD_ID_HERE with the ID from your dashboard URL:

    easyboard.live/d/THIS_IS_YOUR_ID

    Leave TILE_IDS = [] as-is for now — the widget will automatically show the first tile on your dashboard. You can filter to specific tiles later (see below).

  3. 3

    Preview it in Scriptable

    Tap the Run button (triangle). The script fetches your live tile values and shows a small widget preview. If you see a red error message, it will tell you exactly what went wrong.

  4. 4

    Add it to your home screen

    Long-press your home screen → tap + → search Scriptable. Pick a size:

    Small

    Shows the first tile — title, big value, last updated time

    Medium

    Shows the first two tiles side by side

    Tap the widget once in the edit view → set Script to EasyBoard. Tap Done.

Choosing specific tiles (optional)

By default the widget shows your first tile(s). To pick which tiles appear, set an Alternate ID on a tile in EasyBoard (open the tile's edit modal → Alternate ID field), then add that ID to TILE_IDS in the script:

javascript
const TILE_IDS = ["temperature", "humidity"]; // small uses [0], medium uses [0] and [1]

Alternate IDs are case-sensitive. If an ID doesn't match any tile, that slot shows "—".

Multiple widgets for different dashboards

Want widgets for two different dashboards? Long-press the script in Scriptable → Duplicate, rename it (e.g. EasyBoard — Shop), and change DASHBOARD_ID at the top. Each script becomes a separate widget on your home screen.

Troubleshooting

Red error message on widget

The script shows the real error text. "API error: not found" means the Dashboard ID is wrong. A network error means your phone had no connection at last refresh.

Widget shows "---" for a tile value

TILE_IDS has an entry that doesn't match any tile's alternateId. Check for typos, or leave TILE_IDS = [] to fall back to the first tile automatically.

Widget is stale

iOS schedules widget refreshes and doesn't guarantee the 15-minute hint. Tap the widget to open the live dashboard in your browser for real-time data.

Widget still shows old script output

Make sure the widget on your home screen is set to the right Script (long-press widget → Edit Widget → Script).

A note on privacy

The widget reads tile values using EasyBoard's public GET endpoint — no write token needed. Anyone who knows your Dashboard ID can read the tile values. This is fine for most content (metrics, counters, status), but worth keeping in mind if tiles contain sensitive information. The widget never writes to your dashboard.