Convertí un teléfono viejo en un display de escritorio que muestra el clima, tus paneles solares, la hora y el estado de tus servicios. Corre 24/7, se actualiza solo y no cuesta nada: todo vive en un único Worker del plan gratuito de Cloudflare.
(captura en modo demo: los datos solares son de ejemplo, el resto es real)
- Clima — temperatura, sensación térmica, humedad, viento, mínima/máxima, pronóstico por hora y de 5 días. Sale de Open-Meteo (gratis, sin API key). Opcionalmente puede mezclar mediciones de una estación meteorológica local.
- Solar — lo que entregan los paneles, lo que consume la casa, lo que entra o sale de la red, el porcentaje del consumo del día cubierto por el sol, y la curva de la jornada con generación (verde) y consumo (ámbar) superpuestos: donde la ámbar supera a la verde, esa diferencia es lo que se compró de la red. Vía la API de SolaxCloud; el marco del widget se pone verde cuando los paneles cubren el consumo y ámbar cuando estás comprando de la red.
- Hora y fecha con salida y puesta del sol.
- Servicios — semáforo con la latencia de las URLs que le indiques. Entiende chequeos HTTP comunes y páginas de estado tipo Statuspage (Claude, GitHub, Cloudflare, etc.).
Sin credenciales de Solax funciona igual, en modo demo con datos de ejemplo: podés deployarlo y verlo andando en dos minutos.
Un solo Worker de Cloudflare hace las dos cosas: sirve el frontend (React + Vite) y expone /api/*, que hace de proxy y cache de las APIs externas.
Teléfono (Fully Kiosk) → Cloudflare Worker → Open-Meteo / SolaxCloud / tus URLs
polling 1-10 min cache 60-600 s
Por qué el proxy y no llamar a las APIs desde el navegador:
- Las credenciales de SolaxCloud nunca salen del servidor.
- Solax no habilita CORS, así que el navegador no puede llamarla directo.
- El cache protege los límites de las APIs aunque tengas varias pantallas abiertas.
Si una API externa falla, el Worker sirve el último dato bueno (hasta 24 h) marcado como viejo, y el widget muestra un ⚠ hace N min en vez de vaciarse.
Necesitás Node 20.19+ o 22.12+ (lo exige Vite 8; con Node 20.0–20.18 la instalación falla) y una cuenta gratuita de Cloudflare.
git clone https://github.com/NicolasRocchia/display-kiosk.git
cd display-kiosk
npm install
npm run devAbrí http://localhost:5173: ya deberías ver el display funcionando con clima real y datos solares de ejemplo.
npx wrangler login
npm run deployAl terminar te da la URL pública (https://display-kiosk.TU-SUBDOMINIO.workers.dev). Esa es la que va a abrir el teléfono.
En wrangler.jsonc, sección vars:
| Variable | Para qué |
|---|---|
LATITUDE / LONGITUDE |
Tu ubicación para el clima. Click derecho en Google Maps copia las coordenadas. |
TIMEZONE |
Zona horaria IANA, ej. America/Argentina/Buenos_Aires. |
MONITOR_URLS |
JSON con los servicios a vigilar: [{"name":"Mi API","url":"https://..."}]. Agregá "type":"statuspage" si la URL es el /api/v2/summary.json de una Statuspage: además del estado, el display nombra el componente afectado cuando hay un incidente. |
LOCAL_STATION |
Estación meteorológica local (opcional, ver abajo). Vacío = solo Open-Meteo. |
Después de tocar este archivo: npm run cf-typegen && npm run deploy.
- Entrá a developer.solaxcloud.com con tu cuenta de SolaxCloud.
- Application → crear una aplicación. Al elegir permisos alcanza con Information Access Service y Data Monitoring Service: son de solo lectura. Dejá sin marcar todo lo que diga Control, así la credencial no puede tocar tu inversor.
- Copiá el Client ID y el Client Secret.
- Cargalos como secrets:
npx wrangler secret put SOLAX_CLIENT_ID
npx wrangler secret put SOLAX_CLIENT_SECRET- Para desarrollo local, copiá
.dev.vars.examplea.dev.varsy completalos ahí.
No hace falta configurar el número de serie: el Worker descubre solo el inversor y la planta asociados a la cuenta.
Si en Windows usás PowerShell, no cargues los secrets con un pipe (
"valor" | npx wrangler secret put ...): le agrega un salto de línea al final y Solax rechaza la credencial con Bad client credentials. Usá el comando interactivo de arriba onpx wrangler secret bulk archivo.json.
Open-Meteo da un pronóstico modelado muy bueno, pero si tenés una estación real cerca, sus mediciones le ganan. El proyecto trae un adaptador de ejemplo: la estación pública de la UTN Facultad Regional San Francisco (Córdoba, Argentina), que además reporta radiación solar.
Para activarla, poné "LOCAL_STATION": "utn-san-francisco" en wrangler.jsonc. Viene desactivada por defecto: solo tiene sentido si estás en esa zona, y así nadie le manda tráfico innecesario a un servicio ajeno.
Para enchufar otra estación, copiá worker/sources/stations/utnSanFrancisco.ts, adaptá el parseo a su formato y sumá la entrada en stations/index.ts. El clima queda híbrido solo: lo medido lo pone la estación, lo modelado (icono, UV, pronósticos) Open-Meteo, y si la estación se cuelga todo cae a Open-Meteo sin que se note.
Cualquier Android en desuso sirve. La app es Fully Kiosk Browser (la versión gratuita alcanza):
- Instalala desde Play Store y poné tu URL de
workers.devcomo Start URL. - Activá Keep Screen On, Launch on Boot y Restart after Crash.
- En Web Content Settings, activá Auto Reload on Error para que se recupere solo si se corta el WiFi.
- Activá el modo fullscreen / immersive para ocultar las barras del sistema.
- En Xiaomi/MIUI (y varias capas chinas): permitile autostart a Fully y sacale la optimización de batería, o el sistema la va a matar de madrugada.
El display se cuida solo: recarga completa todos los días a las 04:30 para evitar fugas de memoria, y cada 10 minutos chequea si hay una versión nueva deployada y se actualiza sin que toques el teléfono.
Cuidado con la batería: un teléfono enchufado 24/7 se hincha con el tiempo. Lo ideal es un enchufe inteligente con horarios, para que cargue solo unas horas por día.
Si el teléfono no abre la URL y da un error de DNS o de SSL: algunos ISP filtran el dominio
workers.devcompleto. La solución más rápida es ponerle un DNS privado al teléfono (Ajustes → Conexión y compartir → DNS privado →dns.google). La definitiva es apuntar un dominio propio al Worker.
Pensado para mirarse todo el día sin molestar:
- Fondo negro puro: en pantallas AMOLED los píxeles negros están apagados, así que consume menos y no genera brillo.
- Todo el contenido se desplaza unos píxeles cada 5 minutos para evitar el burn-in.
- Tipografías en
vmin: se adapta solo a la orientación y al tamaño de cualquier pantalla. - El color se usa para el estado (verde/ámbar/rojo), nunca como decoración, y siempre acompañado de texto.
La arquitectura está pensada para que sumar una fuente sea corto:
- Worker: creá
worker/sources/loQueSea.tscon una función que devuelva datos ya normalizados y registrala enworker/sources/index.tscon su TTL de cache. Eso te da/api/loQueSeacon cache, manejo de errores y datos viejos servidos ante caídas. - Frontend: creá
src/widgets/LoQueSeaWidget.tsxusando el hookuseWidgetData, agregalo asrc/widgets/registry.tsy definí su área en la grilla desrc/theme.css.
Esto es importante y conviene decidirlo antes de deployar, no después.
La URL de tu Worker es pública y no tiene login. Cualquiera que la conozca ve lo mismo que tu display. Y eso es más de lo que parece: el consumo eléctrico en tiempo real delata si hay alguien en la casa (300 W sostenidos es una casa vacía; 2 kW es alguien cocinando), el clima revela tu zona, y la lista de servicios muestra qué tenés en producción.
Qué hacer al respecto, de menor a mayor esfuerzo:
- Mínimo: dejá el nombre del Worker en algo no adivinable (no
display-kiosk) y no publiques la URL. El repo incluye unrobots.txtque pide a los buscadores no indexarla. - Recomendado si te importa: poné Cloudflare Access delante del Worker (gratis hasta 50 usuarios) y autorizá solo tu cuenta. El teléfono se autentica una vez y queda.
- Contra el abuso: no hay rate limiting. Alguien con la URL puede gastarte las 100.000 requests diarias del plan gratuito y dejar el display sin servicio hasta el otro día. Una regla de rate limiting de Cloudflare, o el mismo Access, lo resuelven.
Sobre MONITOR_URLS: esas URLs las consulta el Worker desde la red de Cloudflare, no desde tu casa, así que no sirven para chequear servicios de tu red local. Y aunque la respuesta de la API no publica las URLs (solo el nombre y la latencia), evitá poner tokens en la query string de un health check.
Sobre las credenciales de Solax: creá la aplicación solo con permisos de lectura. Si marcás los servicios de control, esa credencial puede operar tu inversor — y una credencial de más permisos es un problema más grande el día que se filtre.
Con el plan gratuito de Cloudflare y una sola pantalla, esto no cuesta nada y queda lejísimos de cualquier límite:
- Cloudflare Workers: 100.000 requests/día gratis; el display usa unos 4.500. Los archivos estáticos no cuentan.
- Open-Meteo: gratis para uso no comercial; el cache lo consulta 1 vez cada 10 minutos.
- SolaxCloud: el paquete gratuito del portal permite 100 llamadas por minuto; el display usa 2 por minuto. Ojo que el inversor sube datos a la nube cada ~5 minutos, así que ese es el límite real de frescura del dato, no el polling.
Es un proyecto personal, hecho para andar en mi escritorio. Funciona, pero está probado en un solo escenario: un inversor Solax monofásico residencial sin batería, en Argentina. El código de baterías está preparado en los tipos pero no implementado (en la API de Solax son otro dispositivo, con su propia consulta).
Si lo usás y encontrás algo, los issues y PRs son bienvenidos.
MIT — hacé lo que quieras con esto.
