Saltar al contenido principal

CUP=X

Widgets

Tasas de cambio de divisas en tiempo real directamente en tu pantalla de inicio de iOS/iPadOS/macOS con nuestros widgets personalizados potenciados por Scriptable JS.

info

Prerequisitos

Necesitarás la aplicación Scriptable instalada en tu dispositivo iOS/iPadOS para crear estos widgets.

Scriptable es una potente aplicación de iOS/iPadOS que te permite crear widgets personalizados con JavaScript, automatizaciones y más.

Descargar Scriptable desde la App Store

Widgets de Tasas de Cambio Personalizados

Crea widgets hermosos y personalizables que muestran las tasas de cambio del Peso Cubano (CUP) en tiempo real directamente en tu pantalla de inicio de iOS/iPadOS/macOS.

Ejemplo de Widget de Tasas de Cambio

Widgets para iOS

Características del Widget

update

Actualizaciones en Tiempo Real

Los widgets obtienen las últimas tasas de cambio de CUP=X durante todo el día.

palette

Diseño Personalizable

Elige entre diferentes tamaños y diseños de widget para mostrar.

touch_app

Interactivo

Toca el widget para abrir el sitio web de CUP=X para información más detallada.

Cómo Instalar tu Widget

1

Instalar Scriptable

Descarga e instala la aplicación Scriptable desde la App Store.

2

Copiar el Código del Widget


    /* --------------------------------------------------------------
    Description:
    iOS/iPad Scriptable widget - CUP=X Informal Currency Exchange Rates in Cuba
    Source: elToque: https://eltoque.com/tasas-de-cambio-de-moneda-en-cuba-hoy
    
    Version:
    1.0.0
    -------------------------------------------------------------- */
    const widgetUrl = 'https://peso-cubano.com';
    const apiUrl = `https://api.cambiocuba.money/api/v1/x-rates-by-date-range?trmi=true&period=7D`;
    const currencies = ['ECU', 'USD', 'MLC', 'CAD', 'MXN'];
    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, ECU, MLC, CAD, MXN } = 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, 0)} 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(ECU, 0)} 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, 0)} 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, 0)} 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, 0)} CUP`);
    mxnRateText.textColor = Color.black();
    mxnRateText.font = Font.boldMonospacedSystemFont(16);
    
    // footer
    const date = new Date();
    const df = new DateFormatter();
    df.useFullDateStyle();
    
    const hf = new DateFormatter();
    hf.useShortTimeStyle();
    
    // add date and time
    const dateText = w.addText(`${df.string(date)} ${hf.string(date)}`);
    dateText.textColor = Color.black();
    dateText.font = Font.semiboldSystemFont(10);
    dateText.leftAlignText();
    
    return w;
    }
    
    // assemble small widget layout
    function createSmallWidget(data, resources, widgetUrl) {
    const { USD, ECU, MLC } = 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(`= ${format(USD, 0)} 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(`= ${format(ECU, 0)} 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(`= ${format(MLC, 0)} CUP`);
    mlcRateText.textColor = Color.black();
    mlcRateText.font = Font.boldMonospacedSystemFont(14);
    
    mlcStack.addSpacer();
    
    // footer
    const date = new Date();
    const df = new DateFormatter();
    df.useShortDateStyle();
    
    const hf = new DateFormatter();
    hf.useShortTimeStyle();
    
    const dateText = w.addText(`${df.string(date)} ${hf.string(date)}`);
    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, ECU, 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(`= ${format(USD, 0)} CUP`);
    usdRateText.textColor = Color.white();
    usdRateText.font = Font.mediumMonospacedSystemFont(15);
    
    const eurRateText = stack2.addText(`= ${format(ECU, 0)} CUP`);
    eurRateText.textColor = Color.white();
    eurRateText.font = Font.mediumMonospacedSystemFont(15);
    
    const mlcRateText = stack2.addText(`= ${format(MLC, 0)} 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 getRate(currency) {
    const url = `${apiUrl}&cur=${currency}`;
    const req = new Request(url);
    const res = await req.loadJSON();
    const { min, max, avg, median } = res[res.length - 1];
    
    console.log(`${currency} - Min: ${min}, Max: ${max}, Avg: ${avg}, Median: ${median}`);
    
    return median;
    }
    
    async function getData() {
    const rates = {};
    const ratePromises = currencies.map(currency => getRate(currency));
    const results = await Promise.all(ratePromises);
    for (let i = 0; i < currencies.length; i++) { rates[currencies[i]]=results[i]; } return rates; } 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, fractionDigits = 2) {
        return Math.round(Number.parseFloat(value)).toFixed(fractionDigits);
        }
    
        //initialize
        await init();

Scriptable JS
3

Crear un Nuevo Script en Scriptable

Abre Scriptable, toca el botón + para crear un nuevo script y pega el código.

4

Agregar Widget a la Pantalla de Inicio

Mantén presionada tu pantalla de inicio, toca el botón +, busca Scriptable, selecciona un tamaño de widget, agrégalo, luego selecciona el script que creaste.