SDK
La pantalla de apps de Miblo: página, tarjetas, marcos y capas
La pantalla de apps de Miblo: screen.page, data y quality y, de la forma antigua y obsoleta, card, frame, layer, show, peek y clear, el desgaste de la flash y cada error.
La pantalla de apps
La rotación de pantallas del Miblo Desk (Resumen, Límites, Sesiones) tiene la pantalla de apps, que dibuja tu programa. Necesita Miblo 1.30 o más nuevo en el computador y en el Miblo Desk: un dispositivo más antiguo no recibe nada más que su actualización (miblo status y Miblo Hub lo dicen), y en gadgets responde offline. La pantalla puede ser:
una página web (Miblo 1.45, la forma estándar): HTML, CSS y JavaScript que Miblo Hub dibuja en el computador (
page());
Las tres siguientes son la forma antigua, obsoleta desde Miblo 1.45 pero funcional:
una tarjeta: un título y hasta cuatro elementos (un número grande, un anillo, una barra, una línea de texto, un gráfico diminuto), dibujada por el propio Miblo con sus fuentes y colores;
un marco: una imagen completa de 240x240, guardada en la flash de Miblo;
una capa: la tarjeta dibujada sobre un marco. Es el camino correcto hacia una pantalla hermosa: el fondo va una vez, luego solo cambia el número.
Reglas que siempre se cumplen:
Miblo va primero. Las alertas, "te necesita", la configuración y los códigos siempre se dibujan sobre la pantalla de apps.
El nombre de tu programa siempre está en la pantalla, para que la persona sepa de quién es. Es el
toolque pasas ascreen().Cada programa tiene su propia pantalla. Con varias apps, la pantalla de apps muestra una a la vez y pasa a la siguiente después del tiempo propio de cada una (
miblo apps dwell <id> <seconds>, de 5 a 120; 15 s por defecto). Un tiempo por app, ninguno global.Los límites son los de este computador, no los de tu programa. Cada tasa de esta página (tarjetas,
show,peek,request, marcos, animaciones) es un balde en el puente, compartido por todos los programas y todas las apps oficiales que estén activadas. Ejecuta dos ejemplos seguidos y el segundo puede recibirrate_limited: esperaretryAftersegundos.Cuándo se muestran las apps lo decide la persona (
miblo apps when, la página del Miblo Desk o Miblo Hub): cuando nadie está trabajando (por defecto: tras 60 s sin ninguna sesión de IA corriendo ni esperando), siempre en la rotación, cuando una app pide la pantalla (solo cuando tu programa llama arequest(), abajo) o solo cuando yo lo pida (fijada o un vistazo).
screen()
const screen = miblo.screen({ tool: 'steps' });screen = miblo.screen("steps")tool sigue las reglas del tool de una sesión (de 1 a 20 caracteres, no un nombre de Claude Code, sin _ al inicio). Cada escritura lleva el encabezado x-miblo-sdk-tool; el paquete lo envía por ti. La pantalla de un programa es la de su app declarada (ver Apps), si no, la de su nombre.
page(), data() y quality()
Desde Miblo 1.45, la forma estándar de dibujar la pantalla es una página web: Miblo Hub la renderiza a 240x240 en el computador y transmite al Miblo Desk solo los píxeles que cambiaron. La guía completa, con el manifest, window.miblo y las limitaciones, está en Apps del Desk de página.
await screen.page('weather.html', { data: { city: 'Recife', temp: 29 }, quality: 'auto', max: 'high' });
await screen.data({ city: 'Recife', temp: 30 }); // the page hears it with miblo.on('data')
await screen.quality('ultra'); // or the page itself: miblo.quality('ultra')
await screen.clear();page(file, { data, quality, max }):filees un archivo.htmlrelativo a la carpeta de trabajo del programa (la página de una app del Desk instalada siempre es un archivo de su propio paquete); su CSS, imágenes, fuentes, scripts y videos están a su lado y se cargan por ruta relativa.dataes JSON, de hasta 16 KB, y llega a la página comowindow.miblo.data. Devuelve{ ok, page, quality, max, current }.data(obj): JSON nuevo para la página (hasta 16 KB); la página lo escucha conmiblo.on('data', fn).quality(q, { max }):'auto'(por defecto),'verylow'(objetivo de 30 fps),'low'(25),'medium'(20),'high'(15) o'ultra'(cerca de 10).autoempieza enmedium, baja cuando el flujo se atasca y sube tras unos 5 s limpios, nunca por encima demax.Error nuevo:
no_page(estado 409), cuandodata()oquality()vienen antes depage().
Por ahora solo el paquete de TypeScript (@miblo/status) tiene estos métodos; Python todavía no, pero la ruta POST /sdk/v1/screen/page (y screen/data y screen/quality) está en la referencia.
card()
await screen.card({
v: 1,
title: 'Steps',
icon: 'steps',
items: [
{ t: 'big', value: '8,432', label: 'today' },
{ t: 'ring', value: 0.61, label: 'goal 14k', color: 'amber' },
],
});screen.card({
"v": 1,
"title": "Steps",
"icon": "steps",
"items": [
{"t": "big", "value": "8,432", "label": "today"},
{"t": "ring", "value": 0.61, "label": "goal 14k", "color": "amber"},
],
})Los campos de la tarjeta
| Campo | Reglas |
|---|---|
v | Siempre 1. |
title | Opcional, hasta 24 caracteres, en la parte superior de la pantalla (el nombre de tu programa lo sigue). |
items | De 1 a 4 elementos, de arriba hacia abajo. |
icon | Opcional, un nombre del conjunto de abajo. Un nombre fuera de él se dibuja como ninguno. |
bg | Opcional, "frame:0" a "frame:3": dibuja la tarjeta sobre ese marco (una capa). layer() lo completa por ti. |
La tarjeta completa pesa como máximo 1024 bytes. Los campos desconocidos se descartan; un tipo de elemento desconocido rechaza la tarjeta.
Los elementos
t | Campos | fx que acepta | Cómo se muestra |
|---|---|---|---|
big | value (texto o un número entero, hasta 10 caracteres, obligatorio), label?, color? | count, pulse, blink, rain | Un número grande con una leyenda. |
ring | value (0 a 1), label?, color?; en vivo: id, range?, live | sweep, orbit (+ speed 0 a 360), pulse, blink, rain | Un anillo que se llena, como los anillos de los límites. |
bar | value (0 a 1), label?, color?; en vivo: id, range?, live | sweep, pulse, blink, rain | Una barra que se llena. |
text | value (hasta 40 caracteres, obligatorio), color? | ticker, pulse, blink, rain | Una línea de texto. |
spark | values (2 a 24 números), label?, color?; en vivo: id, range, live (y values pasa a ser opcional) | slide, pulse, blink, rain | Un gráfico de líneas diminuto. |
mesh | mesh ('cube', 'icosphere', 'miblo' o tus propios vértices), shading?, speed?, light?, color? | spin, tumble | Un modelo 3D que el Miblo hace girar; uno por tarjeta, nunca dentro de un row. |
row | items: exactamente 2 de los demás (nunca un row ni un mesh) | ninguno | Dos elementos lado a lado. |
label: hasta 16 caracteres.fx: una palabra, un efecto que el propio Miblo calcula (sin archivo, sin escritura en la flash). Una palabra que el elemento no acepta rechaza la tarjeta (bad_request). Lo que hace y cuesta cada uno, y los campos demesh, están en la guía de Animaciones de pantalla (capítulo 5).id,rangeylive: un gráfico en vivo (Miblo 1.27), alimentado por push(): ver abajo.color:amber,blue,green,red,whiteogrey. Sin uno, cada tipo tiene el suyo (big blanco, ring coral, bar violeta, spark azul, text gris). No hay colores hexadecimales en la v1.icon:steps,heart,water,coffee,sun,moon,cloud,bolt,check,star,clock,calendar,chart,music,mail,code.
Cómo se trata el texto
Las secuencias de espacios se vuelven uno solo, se eliminan los caracteres invisibles, de control y bidi, se recortan los extremos y cada cadena se corta en su límite.
Las frases de Miblo se rechazan (
bad_request): una cadena que empieza como uno de los avisos de Miblo (approved:,phone:,miblo:,allow?,task:y sus formas en portugués) o que usa las frases de alerta del Miblo Desk ("NEEDS YOU", "Asked permission", "Asked a question" y sus traducciones). La pantalla de apps nunca se hace pasar por una alerta de Miblo.El campo con el problema llega en
field, con una forma comoitems[1].items[0].value.
La vida de una tarjeta
Una tarjeta vive en la memoria del Miblo Desk, no en su flash: cambiarla cada minuto no cuesta nada. Cuando el Miblo se reinicia, el puente envía la tarjeta de nuevo dentro de 15 minutos. Una tarjeta que no se actualiza durante 24 horas se descarta (la pantalla muestra "sem dados" con el nombre de tu programa).
Devuelve { ok, current, delivered, gadgets }: current dice si tu pantalla es la que está ahora en la pantalla de apps. Tarjetas: 2 por segundo, ráfagas de 5.
push()
Un gráfico que cambia todo el tiempo (CPU, audio, un sensor) tiene dos modos desde Miblo 1.27. En casi tiempo real, el valor viaja con cada tarjeta y el fx del elemento anima el cambio en el Miblo: simple, pero cada tarjeta es una solicitud que le cuesta al Miblo Desk de 40 a 400 ms, y un elemento con un efecto llega a unos 10 fps en una tarjeta simple (5 bajo una animación). En el modo fluido, el elemento declara live y las muestras pasan por push(), a la tasa que quieras:
await screen.card({ v: 1, title: 'CPU', items: [
{ t: 'ring', id: 'cpu', value: 0.4, range: [0, 100], label: 'now', live: { hz: 10, seconds: 3 } },
{ t: 'spark', id: 'hist', range: [0, 100], label: 'last 6 s', live: { hz: 10, seconds: 3 } },
] });
screen.push('cpu', 42.5); // or screen.push({ cpu: 42.5, hist: 42.5 })screen.push("cpu", 42.5) # or screen.push(cpu=42.5, hist=42.5)El paquete guarda las muestras con su hora y las envía al puente cada 500 ms (o cada 32 muestras). El puente las reúne en lotes de seconds (3 s): remuestrea la ventana a hz × seconds valores, cuantiza cada uno dentro del range y envía una solicitud por lote al Miblo, que reproduce el lote con su propio reloj, a 24 a 30 fps, redibujando solo el rectángulo del gráfico.
El búfer es necesario: el Miblo solo reproduce cuando tiene un lote completo y el siguiente ya viene en camino. Cuesta un retraso de 3 s, y eso se compensa porque el siguiente lote se carga en segundo plano mientras se muestra el actual: la reproducción nunca espera a la red. Un lote tardío mantiene la última muestra (late); dos lotes en la cola descartan el más antiguo.
live: { hz, seconds }:hzde 1 a 30 (por defecto 10),secondsde 1 a 5 (por defecto 3). Solo enspark,ringybar, y como máximo 2 elementos en vivo por tarjeta (fuera o dentro de unrow): un tercero se rechaza (live_too_many). Elfxen un elemento en vivo se ignora.id: de 1 a 16 caracteres[a-z0-9_-], único en la tarjeta, obligatorio conlive: es la clave que usapush().range: [min, max]: números finitos,min < max; obligatorio en unspark,[0, 1]por defecto en un ring y un bar. Las muestras se cuantizan dentro de él.Un lote por tarjeta: los elementos en vivo de una tarjeta comparten los mismos
seconds(uno distinto se rechaza,fielditems[1].live.seconds), y sushz × secondssuman 180 como máximo (dos elementos a 30 Hz × 3 s lo cumplen; 30 Hz × 5 s dos veces se rechaza).push()hasta 60 veces por segundo porid(más rápido se fusiona). El paquete envía lo que está esperando cada 500 ms o 32 muestras, comoPOST /sdk/v1/screen/livecon 1 a 128 muestras, cada una de máximo 60 s de antigüedad; el puente acepta 10 de estas por segundo (ráfagas de 20) y responde{ ok, accepted, unknown }(unknown: ids para los que tu tarjeta actual no tiene un elemento en vivo, descartados). Un lote por Miblo cadaseconds, hasta 1 KB. Nada va a la flash.push()nunca lanza un error: un envío que el puente rechazó va ascreen({ tool, onPushError })(Python:screen(tool, on_push_error=...)), si no, se descarta.await screen.flush()envía lo que está esperando ahora y lanza lo que un envío en segundo plano habría reportado.screen.status()llevalive: una entrada porid, coniyr(su lugar en la tarjeta),hz,seconds,seq,value(la última muestra),buffered(lotes en mano, 0 a 2),playing,lateyreal: { fps, drawMs }, lo que mide el Miblo.stop(),clear()y una tarjeta nueva sin elidterminan el flujo.
La guía de Animaciones de pantalla tiene el capítulo "Gráficos en vivo: dos modos", con una receta y la tabla de cuál modo elegir; la app Demo ao vivo de la tienda de apps del Desk muestra ambos lado a lado.
frame()
Una imagen de 240x240 en uno de los 4 espacios de la flash de Miblo.
import fs from 'node:fs';
await screen.frame(0, fs.readFileSync('background.png'));with open("background.png", "rb") as f:
screen.frame(0, f.read())Lo que puedes enviar:
un PNG (de 8 bits, RGB o RGBA, no entrelazado, hasta 4096 de lado y 4 millones de píxeles en total): se ajusta a 240x240 manteniendo sus proporciones;
píxeles RGBA crudos:
{ rgba, w, h }(TS, con unUint8Array) o{"rgba": bytes, "w": ..., "h": ...}(Python);un archivo MFRM1 listo (el formato de Miblo).
El paquete envía un PNG o píxeles RGBA como base64 dentro de una solicitud de máximo 1 MiB, así que en la práctica un archivo PNG debe quedar por debajo de unos 760 KB y el RGBA crudo por debajo de unos 196.000 píxeles (443x443); más allá la respuesta es too_large (413). Envía un PNG de 240x240 y nada de esto importa.
El computador convierte la imagen a como máximo 64 colores, la codifica por filas y lo verifica todo antes de enviar; no hace falta ningún otro software. Un SVG o HTML lo renderizas tú mismo (un canvas, sharp, resvg...) y envías el PNG. Un fondo de colores planos y texto pesa de 5 a 30 KB; una foto suele pasar de 60000 bytes tras la reducción y se rechaza con su tamaño (too_many_colours).
Devuelve { ok, slot, bytes, colours, delivered, gadgets }. Una subida puede tardar hasta 60 s (el archivo va a cada dispositivo en trozos).
La flash se desgasta
La flash de Miblo admite un número limitado de escrituras, y un marco se escribe en ella en cada subida. Por eso:
cada espacio admite 1 escritura cada 10 s y 100 al día (
rate_limitedconretryAfter, verificado en el computador antes de convertir o enviar nada); la cuenta sobrevive a un reinicio;lo que cambia va en la tarjeta, no en el marco. Una capa redibuja la tarjeta sin escribir nada;
un marco dañado o incompleto se elimina, nunca se dibuja.
Los espacios son compartidos
Los 4 espacios pertenecen a todos los programas. Un espacio es del programa que lo escribió hasta que lo suelta (removeFrame(), clear()) o su pantalla desaparece. Escribir en el espacio de otro programa da slot_taken, con el nombre del dueño en owner.
layer()
La tarjeta dibujada sobre un marco: escribe el fondo una vez, luego envía solo tarjetas.
await screen.layer(0, { v: 1, title: 'Steps', items: [{ t: 'big', value: '8,432', label: 'today' }] });screen.layer(0, {"v": 1, "title": "Steps", "items": [{"t": "big", "value": "8,432", "label": "today"}]})Es card() con bg: "frame:0". Diseña el fondo dejando espacio para los elementos: el diseño de la tarjeta es fijo según la cantidad de elementos.
Una tarjeta se dibuja sobre un marco solo cuando lo dice (bg: "frame:N"): un marco en un espacio por sí solo no se muestra. Lo mismo vale para el run() de una app que devuelve un frame: el ejecutor escribe la imagen, y la tarjeta que devuelve necesita bg: "frame:N" con el mismo espacio.
show()
Fija tu pantalla: se queda en la pantalla de apps (sin rotación) y el Miblo Desk se queda en la pantalla de apps, como miblo mode app. show(false) la suelta.
await screen.show(true);screen.show(True)Devuelve { ok, shown, delivered, gadgets }. Una vez cada 10 s, para todos los programas juntos.
Una fijación retiene la pantalla para todos: mientras tu pantalla está fijada, el animate() de otro programa recibe anim_taken (retryAfter 60). Suelta (show(false)) cuando termines, sobre todo entre dos ejemplos.
peek()
"Mira ahora": la pantalla de apps pasa al frente por 1 a 10 segundos (por defecto 10), a menos que Miblo esté alertando. Para el momento que importa (el bitcoin se movió un 5 %, el build se rompió).
await screen.peek(10);screen.peek(10)Necesita una tarjeta primero (
no_card).Una vez cada 10 minutos por programa, y como máximo una cada 10 s (ráfagas de 3) para todos los programas juntos (
rate_limitedconretryAfter).Trae tu pantalla al frente primero, a menos que otra esté fijada.
Devuelve { ok, seconds, delivered, gadgets }.
request() y release()
Desde Miblo 1.29. "Necesito la pantalla": tu programa pide la pantalla de apps por su propio tiempo (de 5 a 120 segundos; sin seconds, el tiempo que la persona le dio a la app, 15 s por defecto), para el momento que importa (un gol, una reunión en 5 minutos), solo si no está pasando nada importante: ninguna sesión de IA esperando a la persona (un permiso, una pregunta), ninguna alerta de Miblo ni de otra app en la pantalla. Una sesión que simplemente corre no estorba.
const r = await screen.request({ seconds: 30 });
if (!r.shown) console.log('no screen:', r.why);r = screen.request(30)
if not r["shown"]:
print("no screen:", r["why"])La respuesta es honesta, nunca una suposición: { ok, shown: true, seconds, delivered, gadgets } cuando tu pantalla está en el Miblo, o { ok, shown: false, why, seconds, gadgets } con why:
why | Qué pasó |
|---|---|
waiting | Una sesión de IA necesita a la persona ahora mismo |
alert | Una alerta (de Miblo o de una app) está en la pantalla |
manual | La persona eligió "Solo cuando yo lo pida" |
taken | Otra app retiene la pantalla (fijada, o la pidió primero): owner y retryAfter |
hidden | La persona apagó la pantalla de tu app ("Mostrar la pantalla en Miblo") |
refused | El propio Miblo dijo que no (estaba ocupado con algo suyo) |
offline, no_gadget | Ningún Miblo lo recibió (sin conexión, o ninguno emparejado) |
Necesita una tarjeta primero (
no_card). Envía la tarjeta del momento, luego pide la pantalla.Una app a la vez; el mismo programa puede pedir de nuevo solo 60 s después de que terminó su última retención (
rate_limitedconretryAfter). Pedir mientras retienes la pantalla responde los segundos que quedan (held: true), nunca la extiende.La rotación espera; cuando se acaba el tiempo (o con
release()), vuelve la app a la que le tocaba.screen.release()devuelve{ ok, released }.Funciona sea lo que sea que la persona haya elegido, excepto solo cuando yo lo pida; con cuando una app pide la pantalla, es la única forma en que se muestran las apps.
status()dicerequest: { seconds, until }mientras tu pantalla está ahí por una solicitud (nullen otro caso).
clear(), removeFrame() y status()
| TypeScript | Python | Qué hace |
|---|---|---|
screen.removeFrame(slot) | screen.remove_frame(slot) | Elimina uno de tus espacios: { ok, slot, delivered, gadgets }. |
screen.clear() | screen.clear() | Saca tu pantalla: su tarjeta, tus espacios y tu fijación. miblo screen clear saca todas. |
screen.status() | screen.status() | Tu pantalla (abajo). |
screen.budget() | screen.budget() | Lo que tu pantalla le cuesta al Miblo: lo mismo que status().budget. |
status() responde { card, frames, shown, current, area, allowed, rotation, request, anim, live, budget }:
card: tu tarjeta como la guarda el puente (contool), onull;frames: tus espacios,[{ slot, bytes, at }](aten segundos epoch);shown: fijada;current: tu pantalla es la que está ahora en la pantalla de apps;rotation: los segundos que una app se muestra cuando no tiene tiempo propio;area:{ y, h }, la parte de la pantalla de 240x240 que recibe tu contenido. Es{ y: 0, h: 240 }a menos que la persona mantenga la franja de límites arriba ("Mostrar siempre los límites arriba"): diseña tu fondo dentro de ella;allowed:falsecuando la persona apagó la pantalla de tu app ("Mostrar la pantalla en Miblo"). Tu app sigue corriendo para sus alertas, tu tarjeta se conserva pero nunca se muestra, yframe(),animate(),peek(),show()yrequest()responden200conhidden: truey nada entregado. Envía una alerta en vez de dibujar el momento;request:{ seconds, until }mientras una solicitud tuya retiene la pantalla, si nonull;anim: tu animación (conplayingoqueued), si nonull;live: tus gráficos en vivo, arriba;budget: lo que tu pantalla le cuesta al Miblo en RAM, almacenamiento y procesamiento, cada uno en bandas (ok,attention,over) con consejos en portugués e inglés. La memoria de Miblo lo explica;miblo screen statuslo imprime.
Qué hace el Miblo Desk con cada llamada
| Llamada | En el computador | En el Miblo Desk |
|---|---|---|
card() / layer() | Valida, limpia las cadenas, la guarda | Redibuja solo lo que cambió; guardada en memoria |
frame() | Verifica el desgaste, convierte a 64 colores, codifica | Escribe el espacio completo en la flash y lo verifica antes de usarlo |
show() | Marca tu pantalla como fijada | Se queda en la pantalla de apps, sin rotación |
peek() | Verifica los límites | Muestra la pantalla de apps por unos segundos, a menos que esté alertando |
request() | Rechaza lo que ya sabe (manual, una sesión esperando, otra app) | Decide con honestidad: la muestra por los segundos pedidos, o dice por qué no |
clear() | Olvida tu pantalla y tus espacios | Saca la tarjeta y elimina tus marcos |
Un Miblo Desk apagado u ocupado lo pone al día el puente más tarde por sí solo. En gadgets, cada Miblo responde por sí mismo: error es offline, unpaired, rejected, busy o rate_limited (su propio límite de flash). Miblo Hub y Miblo Remote dibujan la misma tarjeta, píxel por píxel, con el código propio del Miblo Desk.
Errores y qué hacer
| Estado | code | Cuándo | Qué hacer |
|---|---|---|---|
| 400 | bad_request (+ field) | Una tarjeta o nombre que las reglas rechazan | Mira field; acorta el texto o cambia la frase |
| 400 | bad_frame | No es un PNG ni un MFRM1 que el conversor acepte (p. ej. un PNG entrelazado) | Guarda como PNG de 8 bits, no entrelazado |
| 409 | slot_taken (+ owner) | El espacio es de otro programa | Usa otro espacio |
| 409 | no_card | Un vistazo, o una solicitud de la pantalla, antes de cualquier tarjeta | Envía una tarjeta primero |
| 400 | live_too_many | Un tercer elemento en vivo en la tarjeta | Dos elementos en vivo por tarjeta como máximo |
| 400 | bad_source, bad_region, bad_fx, bad_option (+ field) | animate(): un archivo que los decodificadores no pueden leer, o una opción fuera de su rango | Mira message; la guía de animaciones lista cada opción |
| 403 | alerts_only | La app declaró screen: false: solo envía alertas | Declara una pantalla, o envía alertas |
| 404 | no_animation | Una vista previa sin ninguna animación tuya | Primero animate() |
| 405 | method_not_allowed | El método HTTP equivocado para la ruta (un cliente directo) | Usa los métodos del paquete |
| 409 | too_many_screens | El puente ya guarda 64 pantallas de programas | screen.clear() las que ya no usas, o miblo screen clear; una pantalla en la que nada escribió durante un día se va sola |
| 409 | anim_taken (+ owner, retryAfter) | La pantalla de otro programa está fijada o en vistazo ahora mismo | Espera retryAfter; si es tu propio otro ejemplo, show(false) allí |
| 409 | no_free_slots | Todos los espacios de marcos los tienen otros programas, o la flash del Miblo está llena | miblo screen clear, o apaga una app |
| 409 | no_animation | mode7() antes de que se animara una imagen | Primero animate(picture, { fx }) |
| 410 | app_off | La app para la que dibuja este programa está apagada (miblo apps disable, Miblo Hub, eliminada) | Enciéndela (miblo apps enable <id>); leer y limpiar siguen funcionando |
| 413 | too_large (+ max) | Una tarjeta de más de 1024 bytes, una solicitud de marco de más de 1 MiB, un MFRM1 de más de 60000 bytes de imagen, una solicitud animate() de más de 12 MiB | Acorta la tarjeta; reduce la imagen |
| 415 | unsupported_media_type | Un marco que no es ni octet-stream ni JSON | Usa el frame() del paquete |
| 422 | too_many_colours (+ bytes) | La imagen es demasiado detallada para un marco | Usa colores planos y menos degradados |
| 422 | too_detailed | Una animación demasiado detallada incluso como cuadros completos, o para un efecto por línea | Menos colores, una región más pequeña |
| 429 | rate_limited (+ retryAfter) | Tarjetas, marcos (el desgaste), show, peek, request o animate por encima del límite, para todos los programas juntos | Espera retryAfter segundos; cambia la tarjeta, no el marco |
| 503 | busy (+ retryAfter) | Se está subiendo el marco o la animación de otro programa | Inténtalo de nuevo en un segundo |
Un 200 con hidden: true no es un error: la persona apagó la pantalla de tu app (ver allowed arriba).
Las tasas, para todo el computador
| Qué | Tasa |
|---|---|
Tarjetas (card(), layer(), una escena guardada reproducida de nuevo) | 2 por segundo, ráfagas de 5 |
show() | 1 cada 10 s |
peek() | 1 cada 10 minutos por programa, y 1 cada 10 s (ráfagas de 3) para todos |
request() | 60 s después de que terminó tu última retención; 1 cada 10 s (ráfagas de 3) para todos |
frame() | 1 escritura por espacio cada 10 s y 100 al día (la flash), y 1 conversión por segundo (ráfagas de 3) |
animate() | 1 animación escrita cada 2 s (ráfagas de 4); 1 decodificación cada 2 s (ráfagas de 4), incluidas las pruebas en seco |
mode7() | 1 por segundo, ráfagas de 3 |
Envíos en vivo (push()) | 10 por segundo, ráfagas de 20 |
Ejemplo completo: una app de pasos
Este ejemplo usa una tarjeta, la forma antigua y obsoleta (mira Apps del Desk de página para la nueva).
El fondo va una vez; luego, cada minuto, solo cambia el número (sin escrituras en la flash).
// MIBLO_TOKEN=$(miblo sdk token) node steps.mjs
import fs from 'node:fs';
import { MibloStatus } from '@miblo/status';
const screen = new MibloStatus().screen({ tool: 'steps' });
await screen.frame(0, fs.readFileSync(new URL('./steps-background.png', import.meta.url)));
await screen.show(true); // or: miblo mode app
let steps = 8432;
for (;;) {
await screen.layer(0, { v: 1, title: 'Steps', icon: 'steps', items: [
{ t: 'big', value: steps.toLocaleString('en-US'), label: 'today' },
{ t: 'ring', value: Math.min(1, steps / 14000), label: 'goal 14k', color: 'amber' },
] });
await new Promise((r) => setTimeout(r, 60_000));
steps += Math.floor(Math.random() * 120);
}# MIBLO_TOKEN=$(miblo sdk token) python3 steps.py
import os, random, time
from miblo_status import MibloStatus
screen = MibloStatus().screen("steps")
with open(os.path.join(os.path.dirname(__file__), "steps-background.png"), "rb") as f:
screen.frame(0, f.read())
screen.show(True) # or: miblo mode app
steps = 8432
while True:
screen.layer(0, {"v": 1, "title": "Steps", "icon": "steps", "items": [
{"t": "big", "value": f"{steps:,}", "label": "today"},
{"t": "ring", "value": min(1, steps / 14000), "label": "goal 14k", "color": "amber"},
]})
time.sleep(60)
steps += random.randint(0, 120)El ejemplo de TypeScript y su imagen de fondo vienen dentro del paquete @miblo/status (examples/steps.mjs y examples/steps-background.png); la copia de allí tiene etiquetas en portugués (Passos, hoje), el mismo programa por lo demás. Hace un bucle infinito por diseño: deténlo con Ctrl+C, luego miblo screen clear (o screen.clear()) para sacar la pantalla.