Hoy voy a mejorar y hacer más eficiente mi escritorio creando un Desk Panel para Home Assistant y dándole más utilidades.
Índice
¿Qué es un «desk panel» y qué me aporta?
La verdad, no sé si este término se aplica en otros ámbitos, pero para mi este «desk panel» (en contraposición a un «wall panel») va a ser un dispositivo táctil que me va a permitir controlar Home Assistant desde el escritorio, y algunas cosas más.
¿Y por qué quiero hacer esto? Muy simple. A medida que he ido enriqueciendo mi instalación me han surgido nuevas necesidades y he hecho avances que me han aportado nuevas funciones, pero también han llenado mi escritorio de ‘gadgets’. Con este dispositivo voy a hacerlo más eficiente y unificar las siguientes funciones:
- El «Habbit Desk» es una de mis creaciones favoritas. Se trata de una pequeña pantalla que me permite controlar mis dispositivos rápidamente desde mi escritorio, además de mostrarme información relevante.
- Tras preocuparme del control de la humedad en la habitación, la información de la temperatura y humedad de calidad es un básico para mi. Además en este caso había aprovechado para añadir un mando zigbee a su lado para controlar la iluminación.
- Recientemente cree un wall panel de invitados, y me encantó (de hecho, a raíz de esa entrada se me ocurrió esta idea). El propósito es tener un panel que el 99% del tiempo me sirva a mí, pero cuando tenga invitados en casa por unos días, tengan un panel intuitivo que les ayude.



Para llevar este proyecto a cabo voy a utilizar un NSPanel Pro 120, que con su pantalla táctil de 4,7″ (funcionando sobre Android) me va a permitir controlar todos los dispositivos e información importante. Además, incorpora sensor de luminosidad, altavoz y micrófono, funciona como ‘hub’ zigbee y matter, es compatible con Google Home y Alexa, y se integra de forma nativa con Home Assistant. De esta forma también quería dar una alternativa sencilla a aquellos que no se manejan con ESPHome.
Instalación y configuración del panel
El primer paso para crear un Desk Panel para Home Assistant con el Sonoff NSPanel Pro 120 es sacarlo de su caja para instalarlo (muy sencillo!). Comprobarás que se trata de una pantalla que en su parte posterior tiene el módulo de conexión. También encontrarás un pequeño manual con el esquema de conexión:
- Conecta el cable neutro a la entrada del terminal identificado con la letra N.
- Conecta el cable de fase a la entrada del terminal identificado con la letra L.
- Una vez realizadas las conexiones y conectado el dispositivo a la corriente, se encenderá de forma automática.


Una vez instalado, procederemos a integrarlo en HA siguiendo estos pasos:
- Lo primero que tendrás que hacer será pulsar en «Start», para seleccionar tu idioma y otros ajustes relacionados.
- A continuación te pedirá que lo conectes a tu red WiFi.
- Hecho esto ya verás el menú principal. Por cierto, algo que me gusta mucho de este dispositivo es que no tiene marcos, ni iconos, por lo que la interfaz queda muy limpia.
- Lo primero que te recomiendo, si no lo has hecho todavía, es que te descargues la aplicación de eWeLink y crees una cuenta. Acto seguido podrás iniciar sesión en tu NSPanel Pro escaneando el QR desde la aplicación en tu dispositivo móvil.
- Desliza la pantalla hacia abajo desde el borde superior para acceder a los ajustes del dispositivo, y baja hasta el apartado «Sobre». Comprueba que tienes el firmware actualizado, ya que necesitarás al menos la versión 3.7.0 para poder mostrar los paneles de Home Assistant de forma nativa (en caso contrario, aplica la actualización disponible).
- Verás que en la parte superior aparece una notificación indicando que ha detectado tu instancia de Home Assistant. Abre la notificación para vincular el dispositivo pulsando en «Añadir» y «Guardar». En este momento se abrirá la pantalla inicial en la que tendrás que logearte. No olvides marcar la casilla para que recuerde tu usuario y contraseña.
- Otra cosa que me gusta del NSPanel Pro 120 es que ni siquiera necesitas un soporte para colocarlo en un escritorio en formato horizontal, ya que puede apoyarse sobre la propia parte trasera y el ángulo de visión es perfecto. Para configurar la pantalla en modo apaisado, desliza la pantalla desde la parte superior, entra en Ajustes > Mostrar > Orientazación de la pantalla > Horizontal.
Configuración del panel en Home Assistant
Una vez que ya hemos instalado el NSPanel Pro 120, ahora vamos a crear el Desk Panel para Home Assistant.
- Ve a Configuración > Paneles de control > Añadir panel de control > Nuevo panel de control desde cero. Dale el título que quieras (por ejemplo, «Invitados») y recuerda NO marcar la opción «Sólo administrador».
- Desde el NSPanel Pro 120, en el menú lateral de HA, pulsa en la parte inferior sobre el nombre del usuario (por ejemplo, «Invitados»). Baja hasta el apartado «Configuración del navegador» y selecciona el panel que acabas de crear en el desplegable «Panel de control». De esta forma conseguiremos que siempre sea el que muestre por defecto en pantalla. Activa también la casilla de «Ocultar siempre el panel lateral».
- Ve a HACS, busca Kiosk Mode, descárgalo y reinicia Home Assistant. Cuando termine, accede desde el menú lateral a tu panel de invitados y pulsa sobre el icono del lápiz de la esquina superior derecha. Ahora pulsa sobre el icono de los 3 puntos que ha aparecido en su lugar, y selecciona la opción «Editor de configuración en bruto». Esto te mostrará el código yaml del panel (que estará prácticamente vacío), para que añadas las siguientes líneas al principio del código.
kiosk_mode: hide_header: true
- Haz clic en «Guardar» y pulsa sobre la «x» a la izquierda del panel para cerrar el modo edición. Observa que la barra superior ha desaparecido del panel, impidiendo que tu invitado acceda a otros apartados de HA (reinicia el dispositivo si hace falta para surta efecto). No obstante, no sufrás, puedes recuperar el acceso al modo edición añadiendo ‘?disable_km’ al final de la URL. Por ejemplo:
Diseño del panel
Como en esta otra entrada ya te di unas pautas básicas sobre qué incluir en un panel de invitados, para no repetirme, esta vez voy a mostrarte exactamente mi configuración y voy a compartir contigo el código.
- Primero he creado un «Modo invitados». Esto es muy sencillo a la par que útil. Te recomiendo que crees un ayudante de tipo alternar (o «boolean») para activar y desactivar el modo invitados (por ejemplo, ‘input_boolean.modo_invitados’). Esto te va permitir adaptar el aspecto del panel en función de si tienes visita. También puedes utilizarlo para modificar el funcionamiento de tus automatizaciones (por ejemplo, del sistema de seguridad).
- Entra en el panel de invitados y crea dos tarjetas condicionales. La idea es que una de ellas aparezca cuando el modo invitados (‘input_boolean.modo_invitados’) este encendido, y la otra cuando esté apagado. Esto te va a permitir aprovechar el panel en cualquier situación.
- Para el panel he instalado desde HACS el tema «Noctis-Solarized». Si quieres asignar este u otro tema al panel, accede al mismo desde el menú lateral y pulsa en el icono del lápiz de la esquina superior derecha para edita (recuerda añadir ‘?disable_km’ al final de la URL). De nuevo, pulsa sobre el lápiz junto al nombre del panel y selecciona este (o el que quieras) en el apartado «Tema».
- Para mi panel de «uso diario» he diseñado un menú tipo cuadrícula aprovechando hasta el último pixel de la pantalla. Con este objetivo, si revisas el código observarás que he ajustado los márgenes y jugado con las dimensiones de las tarjetas usando Card Mod. También he usado otras tarjetas que puedes instalar desde HACS, como Stack in Card, Mushroom Cards y Bar Card. En concreto esta tarjeta muestra:
- Reloj
- Temperatura y humedad de la habitación
- Control de los dispositivos de la habitación
- Valor de ciertos sensores interesantes (como los suscriptores de nuestro canal de YouTube y los miembros de nuestra comunidad de Telegram)
- Próximo evento en el calendario de aguacatec
- Barra de progreso de las impresiones 3D en curso

type: conditional
conditions:
- condition: state
entity: input_boolean.modo_invitados
state: "off"
card:
type: custom:stack-in-card
card_mod:
style: |
ha-card {
background: transparent;
margin-left: -40px;
margin-right: -40px;
border: 0px;
box-shadow: none;
}
cards:
- square: false
type: grid
columns: 3
cards:
- square: false
type: grid
columns: 1
cards:
- type: clock
clock_size: medium
show_seconds: false
time_zone: Europe/Madrid
time_format: "24"
card_mod:
style: |
ha-card {
color: rgba(255, 255, 255, 0.8);
border-radius: 15px!important;
height: 72px!important;
background: rgba(255, 255, 255, 0.05)!important;
}
- type: custom:mushroom-template-card
primary: "{{ states('sensor.youtube_stats')|int }} Sus"
icon: mdi:youtube
secondary: YouTube
icon_color: red
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05)!important;
border-radius: 15px!important;
height: 72px!important;
}
mushroom-shape-icon {
--shape-color: none !important;
}
- type: custom:mushroom-template-card
primary: >-
{% set evento = state_attr('calendar.aguacatec','message') |
string %}
{% if evento == "None" %}
Sin eventos a la vista!
{% else %}
Evento: {{ evento }}
{% endif %}
secondary: >-
{% set evento = state_attr('calendar.aguacatec','message') |
string %} {% if evento == "None" %} Disfruta de las vacaciones
😎 {% else %} {% set inicio =
as_timestamp(state_attr('calendar.aguacatec','start_time'))|timestamp_custom
('%A %d de %b, a las %H:%Mh') |replace('Jan','Ene')
|replace('Aug','Ago') |replace('Dec','Dic')
|replace('Monday','Lunes') |replace('Tuesday','Martes')
|replace('Wednesday','Miércoles') |replace('Thursday','Jueves')
|replace('Friday','Viernes') |replace('Saturday','Sábado')
|replace('Sunday','Domingo') %} El {{ inicio }} {% endif %}
icon: mdi:calendar
icon_color: white
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05)!important;
border-radius: 15px!important;
width: 203%;
height: 72px!important;
}
mushroom-shape-icon {
--shape-color: none !important;
}
ha-state-icon {
color: rgba(255, 255, 255, 0.8);
}
- type: custom:bar-card
height: 71
decimal: 0
positions:
name: inside
icon: inside
indicator: "off"
card_mod:
style: |-
ha-card {
--primary-text-color: white;
background: transparent;
box-shadow: none;
border: 0px;
margin-top: -16px;
width: 220%;
margin-left: -15px!important;
}
bar-card-name {
margin-left: 13px;
font-size: 11px;
text-shadow: 1px 1px #0005;
font-weight: bold;
}
bar-card-currentbar {
border-radius: 15px !important;
clip-path: polygon(0 0, var(--bar-percent) 0,
var(--bar-percent) 100%, 0 100%);
opacity: 0.6;
background: linear-gradient(to right, rgba(230, 242, 162) 0%, rgba(207, 224, 110) 50%, yellowgreen 80%);
}
bar-card-backgroundbar {
border-radius: 15px !important;
opacity: 0.5;
background: rgba(255, 255, 255, 0.3);
}
bar-card-value {
margin-right: 12px;
font-size: 11px;
font-weight: bold;
text-shadow: 1px 1px #0005;
opacity: 0.5;
}
ha-icon {
--mdc-icon-size: 20px;
margin-top: -1px;
margin-left: 15px;
color: rgba(255, 255, 255, 0.8);
}
entities:
- entity: sensor.octoprint_job_percentage
icon: mdi:printer-3d-nozzle
name: Progreso de la impresión
unit_of_measurement: "%"
min: 0
max: 100
- square: false
type: grid
columns: 1
cards:
- type: custom:bar-card
height: 32
decimal: 0
positions:
name: "off"
icon: inside
indicator: "off"
card_mod:
style: |-
ha-card {
--primary-text-color: white;
background: transparent;
box-shadow: none;
border: 0px;
margin-top: -16px;
margin-bottom: -16px;
width: 116%;
margin-left: -15px!important;
}
bar-card-name {
margin-left: 0px;
font-size: 11px;
text-shadow: 1px 1px #0005;
font-weight: bold;
}
bar-card-currentbar {
border-radius: 10px !important;
clip-path: polygon(0 0, var(--bar-percent) 0,
var(--bar-percent) 100%, 0 100%);
opacity: 0.6;
}
bar-card-row:nth-child(1) bar-card-currentbar {
background: linear-gradient(to right, dodgerblue 0%, yellowgreen 50%, crimson 80%);
}
bar-card-row:nth-child(2) bar-card-currentbar {
background: linear-gradient(to right, orange 0%, yellowgreen 30%, dodgerblue 70%);
}
bar-card-backgroundbar {
border-radius: 10px !important;
opacity: 0.7;
background: rgba(255, 255, 255, 0.3);
}
bar-card-value {
margin-right: 12px;
font-size: 11px;
font-weight: bold;
text-shadow: 1px 1px #0005;
opacity: 0.5;
}
ha-icon {
--mdc-icon-size: 20px;
margin-top: -1px;
color: rgba(255, 255, 255, 0.8);
}
entities:
- entity: sensor.sensor_temperatura_despacho_temperature
icon: mdi:thermometer
name: ""
unit_of_measurement: ºC
max: 40
tap_action:
action: more-info
- entity: sensor.sensor_temperatura_despacho_humidity
icon: mdi:water-percent
name: ""
unit_of_measurement: "%"
decimal: 0
min: 0
max: 100
tap_action:
action: more-info
- type: custom:mushroom-template-card
primary: "{{ states('input_number.miembros_telegram')|int }} Mem"
icon: fab:telegram
secondary: Telegram
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05)!important;
border-radius: 15px!important;
height: 72px!important;
}
mushroom-shape-icon {
--shape-color: none !important;
}
ha-state-icon {
color: #1e90ff;
}
- square: false
type: grid
columns: 1
cards:
- type: custom:mushroom-template-card
primary: Lámpara
icon: mdi:ceiling-light
entity: switch.luz_estudio_switch_0
secondary: ""
tap_action:
action: toggle
icon_color: >
{% if states('switch.luz_estudio_switch_0') == "on" %} white {%
endif %}
card_mod:
style: |
ha-card {
{% if states('switch.luz_estudio_switch_0') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Escritorio
icon: mdi:led-strip-variant
entity: light.escritorio
secondary: ""
tap_action:
action: toggle
icon_color: |
{% if states('light.escritorio') == "on" %} white {% endif %}
card_mod:
style: |
ha-card {
{% if states('light.escritorio') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Impresora
icon: mdi:printer
entity: switch.impresora
secondary: ""
tap_action:
action: toggle
icon_color: |
{% if states('switch.impresora') == "on" %} white {% endif %}
card_mod:
style: |
ha-card {
{% if states('switch.impresora') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Impr. 3D
icon: mdi:printer-3d-nozzle
entity: switch.impresora_3d
secondary: ""
tap_action:
action: toggle
icon_color: >
{% if states('switch.impresora_3d') == "on" %} white {% endif
%}
card_mod:
style: |
ha-card {
{% if states('switch.impresora_3d') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
{% if states('switch.impresora_3d') == "Printing"%}
ha-state-icon {
animation: print 1s linear infinite alternate;
}
@keyframes print {
0% { transform: translateX(4px); }
30% { clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 100%, 0 26%); }
100% { transform: translateX(-4px); clip-path: polygon(0 0, 100% 0%, 100% 100%, 40% 100%, 0 26%); }
}
{% endif %}
- Para mi panel de invitados he adaptado los dispositivos controlables, para que sean más útiles a los invitados. Además en incluido un código QR para que tengan acceso directo a mi red WiFi, el calendario de invitados (en el que les mostraré las actividades que hemos planeado para su visita) y una simpática imagen que adapataré en función de quién venga de visita 😊

type: conditional
conditions:
- condition: state
entity: input_boolean.modo_invitados
state: "on"
card:
type: custom:stack-in-card
card_mod:
style: |
ha-card {
background: transparent;
margin-left: -40px;
margin-right: -40px;
border: 0px;
box-shadow: none;
}
cards:
- square: false
type: grid
columns: 3
cards:
- square: false
type: grid
columns: 1
cards:
- type: clock
clock_size: medium
show_seconds: false
time_zone: Europe/Madrid
time_format: "24"
card_mod:
style: |
ha-card {
color: rgba(255, 255, 255, 0.8);
border-radius: 15px!important;
height: 72px!important;
background: rgba(255, 255, 255, 0.05)!important;
}
- type: custom:mushroom-template-card
primary: ""
icon: ""
secondary: ""
card_mod:
style: |
ha-card {
background: url('/local/invitados.jpeg')!important;
background-size: cover!important;
height: 152px!important;
border-radius: 15px!important;
}
- type: custom:mushroom-template-card
primary: >-
{% set evento =
state_attr('calendar.alberto_tb218_hotmail_com','message') |
string %}
{% if evento == "None" %}
Sin eventos a la vista!
{% else %}
Evento: {{ evento }}
{% endif %}
secondary: >-
{% set evento =
state_attr('calendar.alberto_tb218_hotmail_com','message') |
string %} {% if evento == "None" %} Disfruta de las vacaciones
😎 {% else %} {% set inicio =
as_timestamp(state_attr('calendar.alberto_tb218_hotmail_com','start_time'))|timestamp_custom
('%A %d de %b, a las %H:%Mh') |replace('Jan','Ene')
|replace('Aug','Ago') |replace('Dec','Dic')
|replace('Monday','Lunes') |replace('Tuesday','Martes')
|replace('Wednesday','Miércoles') |replace('Thursday','Jueves')
|replace('Friday','Viernes') |replace('Saturday','Sábado')
|replace('Sunday','Domingo') %} El {{ inicio }} {% endif %}
icon: mdi:calendar
icon_color: white
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05)!important;
border-radius: 15px!important;
width: 203%;
height: 72px!important;
}
mushroom-shape-icon {
--shape-color: none !important;
}
ha-state-icon {
color: rgba(255, 255, 255, 0.8);
}
- square: false
type: grid
columns: 1
cards:
- type: custom:bar-card
height: 32
decimal: 0
positions:
name: "off"
icon: inside
indicator: "off"
card_mod:
style: |-
ha-card {
--primary-text-color: white;
background: transparent;
box-shadow: none;
border: 0px;
margin-top: -16px;
margin-bottom: -16px;
width: 116%;
margin-left: -15px!important;
}
bar-card-name {
margin-left: 0px;
font-size: 11px;
text-shadow: 1px 1px #0005;
font-weight: bold;
}
bar-card-currentbar {
border-radius: 10px !important;
clip-path: polygon(0 0, var(--bar-percent) 0,
var(--bar-percent) 100%, 0 100%);
opacity: 0.6;
}
bar-card-row:nth-child(1) bar-card-currentbar {
background: linear-gradient(to right, dodgerblue 0%, yellowgreen 50%, crimson 80%);
}
bar-card-row:nth-child(2) bar-card-currentbar {
background: linear-gradient(to right, orange 0%, yellowgreen 30%, dodgerblue 70%);
}
bar-card-backgroundbar {
border-radius: 10px !important;
opacity: 0.7;
background: rgba(255, 255, 255, 0.3);
}
bar-card-value {
margin-right: 12px;
font-size: 11px;
font-weight: bold;
text-shadow: 1px 1px #0005;
opacity: 0.5;
}
ha-icon {
--mdc-icon-size: 20px;
margin-top: -1px;
color: rgba(255, 255, 255, 0.8);
}
entities:
- entity: sensor.sensor_temperatura_despacho_temperature
icon: mdi:thermometer
name: ""
unit_of_measurement: ºC
max: 40
tap_action:
action: more-info
- entity: sensor.sensor_temperatura_despacho_humidity
icon: mdi:water-percent
name: ""
unit_of_measurement: "%"
decimal: 0
min: 0
max: 100
tap_action:
action: more-info
- type: custom:stack-in-card
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05)!important;
}
cards:
- type: custom:mushroom-template-card
primary: ""
icon: ""
secondary: ""
card_mod:
style: |
ha-card {
background: url('/local/qrwifi.png')!important;
background-position: center!important;
background-repeat: no-repeat!important;
background-size: 120px 120px!important;
height: 152px!important;
}
- square: false
type: grid
columns: 1
cards:
- type: custom:mushroom-template-card
primary: Lámpara
icon: mdi:ceiling-light
entity: switch.luz_estudio_switch_0
secondary: ""
tap_action:
action: toggle
icon_color: >
{% if states('switch.luz_estudio_switch_0') == "on" %} white {%
endif %}
card_mod:
style: |
ha-card {
{% if states('switch.luz_estudio_switch_0') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Mesilla
icon: mdi:lightbulb-night-outline
entity: light.bombilla_escritorio
secondary: ""
tap_action:
action: toggle
icon_color: >
{% if states('light.bombilla_escritorio') == "on" %} white {%
endif %}
card_mod:
style: |
ha-card {
{% if states('light.bombilla_escritorio') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Calefacción
icon: mdi:fire
entity: switch.impresora
secondary: ""
tap_action:
action: toggle
icon_color: |
{% if states('climate.salon') == "heat" %} white {% endif %}
card_mod:
style: |
ha-card {
{% if states('climate.salon') == "heat" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}
- type: custom:mushroom-template-card
primary: Ventilador
icon: mdi:fan
entity: switch.ventilador
secondary: ""
tap_action:
action: toggle
icon_color: |
{% if states('switch.ventilador') == "on" %} white {% endif %}
card_mod:
style: |
ha-card {
{% if states('switch.ventilador') == "on" %}
background: linear-gradient( to top right, rgba(33, 165, 208, 0.6), rgba(59, 190, 188, 0.6) ), black !important;
{% else %}
background: rgba(255, 255, 255, 0.05)!important;
{% endif %}
border-radius: 15px!important;
height: 72px!important;
padding-left: 10px;
}

