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.
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.
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.

Widgets para iOS
Características del Widget
Actualizaciones en Tiempo Real
Los widgets obtienen las últimas tasas de cambio de CUP=X durante todo el día.
Diseño Personalizable
Elige entre diferentes tamaños y diseños de widget para mostrar.
Interactivo
Toca el widget para abrir el sitio web de CUP=X para información más detallada.
Cómo Instalar tu Widget
Instalar Scriptable
Descarga e instala la aplicación Scriptable desde la App Store.
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();
Crear un Nuevo Script en Scriptable
Abre Scriptable, toca el botón + para crear un nuevo script y pega el código.
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.