Skip to main content

CUP=X

Widgets

iPhone iPad Mac

CUP=X Exchange Rates
right on your Pantalla de Inicio.

Free widgets with cuban peso exchange rates. USD, EUR, CAD, MXN and MLC — always visible on your iPhone, iPad, or Mac.

info

Requires Scriptable (free on the App Store) to run the widgets.

Exchange Rate Widget Example

iOS Widgets

update

Real-time

Rates update automatically throughout the day.

palette

Customizable

Choose from different widget sizes and layouts.

touch_app

Interactive

Tap the widget to open CUP=X for detailed info.

How to Install Your Widget

1

Install Scriptable

Download and install the Scriptable app from the App Store.

2

Copy the Widget Code


    /* --------------------------------------------------------------
    Description:
    iOS/iPad/macOS Scriptable widget - CUP=X Informal Currency Exchange Rates in Cuba
    Source: elToque
    Version:
    1.0.0
    -------------------------------------------------------------- */
    const widgetUrl = 'https://peso-cubano.com';
    const apiUrl = 'https://static.peso-cubano.com/el-toque.json';
    const images = [
    {
    name: 'eur',
    url: 'https://peso-cubano.com/img/eur.png'
    },
    {
    name: 'usd',
    url: 'https://peso-cubano.com/img/usd.png'
    },
    {
    name: 'cad',
    url: 'https://peso-cubano.com/img/cad.png'
    },
    {
    name: 'mxn',
    url: 'https://peso-cubano.com/img/mxn.png'
    },
    {
    name: 'mlc',
    url: 'https://peso-cubano.com/img/mlc.png'
    },
    ];
    
    const languages = {
    en: {
    shortTitle: 'CUP=X',
    subtitle: 'Exchange Rates',
    noData: 'No currency exchange rate data.',
    },
    es: {
    shortTitle: 'CUP=X',
    subtitle: 'Tasas de Cambio',
    noData: 'No hay datos de tasas de cambio de divisas.',
    }
    };
    
    async function init() {
    let widget;
    try {
    const data = await getData();
    const resources = await getImages();
    widget = createWidget(data, resources, widgetUrl);
    } catch (err) {
    console.error(`Error getting widget data. ${err}`);
    widget = createEmptyWidget();
    } finally {
    if (config.runsInWidget || config.runsInAccessoryWidget) {
    // create and show widget
    Script.setWidget(widget);
    Script.complete();
    }
    else {
    widget.presentMedium();
    }
    
    }
    }
    
    function createWidget(data, resources, widgetUrl) {
    if (config.runsInAccessoryWidget) {
    return createLockScreenWidget(data, widgetUrl);
    }
    else if (config.widgetFamily === 'small') {
    return createSmallWidget(data, resources, widgetUrl);
    }
    return createMediumWidget(data, resources, widgetUrl);
    }
    
    // assemble empty widget layout
    function createEmptyWidget() {
    const w = new ListWidget();
    w.backgroundColor = Color.white();
    w.url = widgetUrl;
    
    w.addSpacer();
    
    const infoText = w.addText(getNoDataText());
    infoText.textColor = Color.black();
    infoText.font = Font.boldMonospacedSystemFont(20);
    infoText.centerAlignText();
    
    const runsInSmallWidget = config.widgetFamily === 'small';
    
    if (runsInSmallWidget || config.runsInAccessoryWidget) {
    const fontSize = runsInSmallWidget ? 18 : 14;
    infoText.font = Font.boldMonospacedSystemFont(fontSize);
    infoText.leftAlignText();
    }
    
    w.addSpacer();
    
    return w;
    }
    
    // assemble medium widget layout
    function createMediumWidget(data, resources, widgetUrl) {
    const { USD, EUR, MLC, CAD, MXN, createdAt } = data;
    
    const w = new ListWidget();
    w.backgroundColor = Color.white();
    w.url = widgetUrl;
    
    w.addSpacer(2);
    
    // header
    const headerStack = w.addStack();
    headerStack.layoutHorizontally();
    headerStack.centerAlignContent();
    headerStack.spacing = 8;
    
    // title
    const staticText = headerStack.addText(getTitleText());
    staticText.textColor = Color.black();
    staticText.font = Font.boldSystemFont(18);
    staticText.centerAlignText();
    
    w.addSpacer(4);
    
    // body
    const mainStack = w.addStack();
    mainStack.layoutHorizontally();
    mainStack.topAlignContent();
    mainStack.setPadding(0, 4, 0, 4);
    
    const leftStack = mainStack.addStack();
    leftStack.layoutVertically();
    leftStack.topAlignContent();
    
    // usd
    const usdStack = leftStack.addStack();
    usdStack.layoutHorizontally();
    usdStack.centerAlignContent();
    usdStack.spacing = 8;
    
    const usdImage = usdStack.addImage(getImage(resources, 'usd'));
    usdImage.imageSize = new Size(32, 32);
    
    const usdRateText = usdStack.addText(`= ${format(USD)} CUP`);
    usdRateText.textColor = Color.black();
    usdRateText.font = Font.boldMonospacedSystemFont(16);
    
    // eur
    const eurStack = leftStack.addStack();
    eurStack.layoutHorizontally();
    eurStack.centerAlignContent();
    eurStack.spacing = 8;
    
    const eurImage = eurStack.addImage(getImage(resources, 'eur'));
    eurImage.imageSize = new Size(32, 32);
    
    const eurRateText = eurStack.addText(`= ${format(EUR)} CUP`);
    eurRateText.textColor = Color.black();
    eurRateText.font = Font.boldMonospacedSystemFont(16);
    
    // cad
    const cadStack = leftStack.addStack();
    cadStack.layoutHorizontally();
    cadStack.centerAlignContent();
    cadStack.spacing = 8;
    
    const cadImage = cadStack.addImage(getImage(resources, 'cad'));
    cadImage.imageSize = new Size(32, 32);
    
    const cadRateText = cadStack.addText(`= ${format(CAD)} CUP`);
    cadRateText.textColor = Color.black();
    cadRateText.font = Font.boldMonospacedSystemFont(16);
    
    
    mainStack.addSpacer();
    
    // right stack
    const rightStack = mainStack.addStack();
    rightStack.layoutVertically();
    rightStack.topAlignContent();
    
    // mlc
    const mlcStack = rightStack.addStack();
    mlcStack.layoutHorizontally();
    mlcStack.centerAlignContent();
    mlcStack.spacing = 8;
    
    const mlcImage = mlcStack.addImage(getImage(resources, 'mlc'));
    mlcImage.imageSize = new Size(32, 32);
    
    const mlcRateText = mlcStack.addText(`= ${format(MLC)} CUP`);
    mlcRateText.textColor = Color.black();
    mlcRateText.font = Font.boldMonospacedSystemFont(16);
    
    // mxn
    const mxnStack = rightStack.addStack();
    mxnStack.layoutHorizontally();
    mxnStack.centerAlignContent();
    mxnStack.spacing = 8;
    
    const mxnImage = mxnStack.addImage(getImage(resources, 'mxn'));
    mxnImage.imageSize = new Size(32, 32);
    
    const mxnRateText = mxnStack.addText(`= ${format(MXN)} CUP`);
    mxnRateText.textColor = Color.black();
    mxnRateText.font = Font.boldMonospacedSystemFont(16);
    
    // footer
    const df = new DateFormatter();
    df.useFullDateStyle();
    
    const hf = new DateFormatter();
    hf.useShortTimeStyle();
    
    // add date and time
    const dateText = w.addText(`${df.string(createdAt)} ${hf.string(createdAt)}`);
    dateText.textColor = Color.black();
    dateText.font = Font.semiboldSystemFont(10);
    dateText.leftAlignText();
    
    return w;
    }
    
    // assemble small widget layout
    function createSmallWidget(data, resources, widgetUrl) {
    const { USD, EUR, MLC, createdAt } = data;
    
    const w = new ListWidget();
    w.backgroundColor = Color.white();
    w.url = widgetUrl;
    
    // header
    w.addSpacer(8);
    
    const staticText = w.addText(getShortTitleText());
    staticText.textColor = Color.black();
    staticText.font = Font.boldSystemFont(12);
    staticText.leftAlignText();
    
    const exchangeText = w.addText(getSubtitleText());
    exchangeText.textColor = Color.black();
    exchangeText.font = Font.boldSystemFont(12);
    exchangeText.leftAlignText();
    
    // body
    const mainStack = w.addStack();
    mainStack.layoutVertically();
    mainStack.centerAlignContent();
    
    // usd
    const usdStack = mainStack.addStack();
    usdStack.layoutHorizontally();
    usdStack.centerAlignContent();
    usdStack.spacing = 4;
    
    const usdImage = usdStack.addImage(getImage(resources, 'usd'));
    usdImage.imageSize = new Size(32, 32);
    
    const usdRateText = usdStack.addText(`= ${formatInt(USD)} CUP`);
    usdRateText.textColor = Color.black();
    usdRateText.font = Font.boldMonospacedSystemFont(14);
    
    usdStack.addSpacer();
    
    // eur
    const eurStack = mainStack.addStack();
    eurStack.layoutHorizontally();
    eurStack.centerAlignContent();
    eurStack.spacing = 4;
    
    const eurImage = eurStack.addImage(getImage(resources, 'eur'));
    eurImage.imageSize = new Size(32, 32);
    
    const eurRateText = eurStack.addText(`= ${formatInt(EUR)} CUP`);
    eurRateText.textColor = Color.black();
    eurRateText.font = Font.boldMonospacedSystemFont(14);
    
    eurStack.addSpacer();
    
    // mlc
    const mlcStack = mainStack.addStack();
    mlcStack.layoutHorizontally();
    mlcStack.centerAlignContent();
    mlcStack.spacing = 4;
    
    const mlcImage = mlcStack.addImage(getImage(resources, 'mlc'));
    mlcImage.imageSize = new Size(32, 32);
    
    const mlcRateText = mlcStack.addText(`= ${formatInt(MLC)} CUP`);
    mlcRateText.textColor = Color.black();
    mlcRateText.font = Font.boldMonospacedSystemFont(14);
    
    mlcStack.addSpacer();
    
    // footer
    const df = new DateFormatter();
    df.useShortDateStyle();
    
    const hf = new DateFormatter();
    hf.useShortTimeStyle();
    
    const dateText = w.addText(`${df.string(createdAt)} ${hf.string(createdAt)}`);
    dateText.textColor = Color.black();
    dateText.font = Font.semiboldSystemFont(8);
    dateText.centerAlignText();
    
    w.addSpacer(8);
    
    return w;
    }
    
    // assemble lock screen widget layout
    function createLockScreenWidget(data, widgetUrl) {
    const { USD, EUR, MLC } = data;
    
    const w = new ListWidget();
    w.url = widgetUrl;
    
    const mainStack = w.addStack();
    mainStack.layoutHorizontally();
    mainStack.centerAlignContent();
    
    // stack 1
    const stack1 = mainStack.addStack();
    stack1.layoutVertically();
    stack1.centerAlignContent();
    stack1.spacing = 4;
    
    const usdText = stack1.addText('USD');
    usdText.textColor = Color.white();
    usdText.font = Font.heavyMonospacedSystemFont(15);
    
    const eurText = stack1.addText('EUR');
    eurText.textColor = Color.white();
    eurText.font = Font.heavyMonospacedSystemFont(15);
    
    const mlcText = stack1.addText('MLC');
    mlcText.textColor = Color.white();
    mlcText.font = Font.heavyMonospacedSystemFont(15);
    
    mainStack.addSpacer(4);
    
    // stack 2
    const stack2 = mainStack.addStack();
    stack2.layoutVertically();
    stack2.centerAlignContent();
    stack2.spacing = 4;
    
    const usdRateText = stack2.addText(`= ${formatInt(USD)} CUP`);
    usdRateText.textColor = Color.white();
    usdRateText.font = Font.mediumMonospacedSystemFont(15);
    
    const eurRateText = stack2.addText(`= ${formatInt(EUR)} CUP`);
    eurRateText.textColor = Color.white();
    eurRateText.font = Font.mediumMonospacedSystemFont(15);
    
    const mlcRateText = stack2.addText(`= ${formatInt(MLC)} CUP`);
    mlcRateText.textColor = Color.white();
    mlcRateText.font = Font.mediumMonospacedSystemFont(15);
    
    return w;
    }
    
    function getTitleText() {
    const lang = Device.language();
    if (!languages[lang]) {
    return `${languages.en.shortTitle} ${languages.en.subtitle}`;
    }
    return `${languages[lang].shortTitle} ${languages[lang].subtitle}`;
    }
    
    function getShortTitleText() {
    const lang = Device.language();
    if (!languages[lang]) {
    return `${languages.en.shortTitle}`;
    }
    return `${languages[lang].shortTitle}`;
    }
    
    function getSubtitleText() {
    const lang = Device.language();
    if (!languages[lang]) {
    return `${languages.en.subtitle}`;
    }
    return `${languages[lang].subtitle}`;
    }
    
    function getNoDataText() {
    const lang = Device.language();
    if (!languages[lang]) {
    return languages.en.noData;
    }
    return languages[lang].noData;
    }
    
    async function getData() {
    const req = new Request(apiUrl);
    const res = await req.loadJSON();
    const { usd, eur, mlc, cad, mxn } = res.rates;
    
    console.log(`USD: ${usd}, EUR: ${eur}, MLC: ${mlc}, CAD: ${cad}, MXN: ${mxn}`);
    
    return { USD: usd, EUR: eur, MLC: mlc, CAD: cad, MXN: mxn, createdAt: new Date(res.createdAt) };
    }
    
    async function getImages() {
    const result = {};
    const imagePromises = images.map(image => {
    const req = new Request(image.url);
    return req.loadImage().then(res => ({ name: image.name, image: res }));
    });
    const responses = await Promise.all(imagePromises);
    for (const r of responses) {
    const { name, image } = r;
    result[name] = image;
    }
    return result;
    }
    
    function getImage(resources, key) {
    return resources[key];
    }
    
    function format(value) {
    const num = Number.parseFloat(value);
    if (Number.isInteger(num)) {
    return num.toString();
    }
    const truncated = Math.floor(num * 100) / 100;
    return truncated.toString();
    }
    
    function formatInt(value) {
    return Math.floor(Number.parseFloat(value)).toString();
    }
    
    //initialize
    await init();

Scriptable JS
3

Create a New Script in Scriptable

Open Scriptable, tap the + button to create a new script, and paste the code.

4

Add Widget to Home Screen

Long press on your home screen, tap the + button, search for Scriptable, select a widget size, add it, then select the script you created.