@Sescandell, estoy haciendo un PR para el hilo piloto para la integración del termostato y mi dispositivo QUBINO solo devuelve un variador y, en función del valor, corresponde a un estado del hilo piloto.
¿Hay alguna manera de tener modos en lugar de un variador en la integración? Te lo agradezco de antemano.
A continuación, un análisis de Claude:
# Qubino Flush Pilot (ZMNHJD): soporte para radiador de cable piloto
> Especificaciones de la integración externa Z-Wave. Se basa en el marco de las
> integraciones externas del monorepo Gladys (`docs/specs/external-integrations/`):
> los dispositivos se publican mediante `POST /discovered_device` (C.3, SDK
> `publishDiscoveredDevices`), los comandos llegan mediante
> `external-integration.device.set-value` (C.4), y los estados se envían mediante
> `POST /state` (B.6).
## 1. Problema
El Qubino Flush Pilot (ZMNHJD) controla un radiador de cable piloto. Lo hace a través de
su **Multilevel Switch** (CC 38): cada rango de niveles corresponde a una
orden de cable piloto. Actualmente, la integración lo publica como lo indica su
clase de dispositivo, es decir, un variador (posición 0–99 %, estado encendido/apagado,
« restaurar el valor anterior »).
Consecuencias:
- el usuario ve « 30 % » cuando el radiador está en **Eco**;
- el termostato no puede controlarlo. Su actuador de cable piloto
(`THERMOSTAT_PILOT_WIRE_FEATURE`, especificación del termostato C.1.1) solo acepta una
funcionalidad `heater` / `pilot-wire-mode`;
- las escenas, el panel y HomeKit ven un variador, no una orden.
La traducción nivel → orden es específica de este producto: por lo tanto, pertenece a
la integración que conoce este producto. El termostato, como el resto de Gladys,
no ve más que el tipo estándar.
## 2. Identificar el dispositivo
| Campo | Valor |
|---|---|
| `manufacturerId` | 345 (`0x0159`, Qubino) |
| `productType` | 4 (`0x0004`) |
| `productId` | 81 (`0x0051`) |
| `deviceId` zwave-js | `345-81-4` |
| `deviceClass` | básico 4, genérico 17, específico 1 |
| configuración zwave-js | `0x0159/zmnhjd.json`, etiqueta `ZMNHJD`, « Flush Pilot » |
**La clase de dispositivo no es utilizable.** Genérico 17 / específico 1
(« Multilevel Switch, variador ») es la clase de todos los variadores. Usarla
transformaría cada variador en un cable piloto. El dispositivo se identifica por su
**identificador de producto** (`manufacturerId` + `productType` + `productId`), verificado
**antes** de cualquier regla basada en la clase de dispositivo.
## 3. Dispositivo publicado
El nodo publica **una sola** funcionalidad accionable en lugar de las del
variador:
| Campo | Valor |
|---|---|
| `category` | `heater` (`DEVICE_FEATURE_CATEGORIES.HEATER`) |
| `type` | `pilot-wire-mode` (`DEVICE_FEATURE_TYPES.HEATER.PILOT_WIRE_MODE`) |
| `min` / `max` | `0` / `5` (`PILOT_WIRE_MODE.OFF` … `PILOT_WIRE_MODE.COMFORT`) |
| `read_only` | `false` |
| `has_feedback` | `true`: el módulo devuelve `currentValue` después de cada cambio, incluido un
presionar sus propios botones |
| `keep_history` | `true` |
| valor fuente | CC 38 `currentValue`, endpoint 0 |
**No publicados** para este producto:
- las funcionalidades del variador (`position`, el `estado` encendido/apagado deducido del
nivel, `restorePrevious`). « Restaurar el valor anterior » enviaría al radiador una
orden arbitraria;
- el Binary Switch explícito (CC 37). La integración ya lo elimina de cualquier nodo
que tenga un Multilevel Switch;
- `Up` / `Down` / `duration` / `event` (CC 38), como hoy en todos los
nodos.
Inalterados: los parámetros de configuración (CC 112: tipos de entrada, modo de las
entradas 11/12/13, estado después de un corte de energía 30). Permanecen fuera del
ámbito (sección 8).
## 4. Escribir una orden (Gladys → dispositivo)
En `external-integration.device.set-value` para esta funcionalidad, `value`
es un `PILOT_WIRE_MODE`. Se escribe en forma de nivel por el comando
`set` del Multilevel Switch (CC 38, endpoint 0):
| Orden (`PILOT_WIRE_MODE`) | Valor | Nivel escrito |
|---|---|---|
| `OFF` (apagado) | 0 | 0 |
| `FROST_PROTECTION` (protección contra heladas) | 1 | 20 |
| `ECO` | 2 | 30 |
| `COMFORT_2` (confort −2 °C) | 4 | 40 |
| `COMFORT_1` (confort −1 °C) | 3 | 50 |
| `COMFORT` (confort) | 5 | 99 |
- Los niveles escritos son los medidos en el módulo: 0 / 20 / 30 / 40 / 50 / 99.
- Cualquier otro valor recibe un **command-result en error** (« orden de cable piloto
desconocida »), y nada se envía al módulo.
- **Sin estado optimista.** El estado se envía cuando el módulo confirma con
`currentValue` (sección 5), no al enviar el comando. Un radiador que ha fallado en
la trama no debe parecer que ha obedecido.
## 5. Leer una orden (dispositivo → Gladys)
Cada actualización de `currentValue` en la CC 38, el nivel se lee **por
rango**, y la orden se envía mediante `POST /state`:
| Nivel | Orden enviada |
|---|---|
| 0–10 | `OFF` |
| 11–20 | `FROST_PROTECTION` |
| 21–30 | `ECO` |
| 31–40 | `COMFORT_2` |
| 41–50 | `COMFORT_1` |
| 51–99 | `COMFORT` |
| otro (por ejemplo, 255, un valor no numérico) | nada se envía |
La lectura por rango en lugar de por valor exacto es indispensable: el módulo
puede devolver cualquier nivel de un rango. Se ha observado un clic en su botón en
**60** antes de 99, y es una orden de confort.
`targetValue` no se lee: `currentValue` es lo que el módulo aplica
realmente.
## 6. Dispositivos creados antes de este cambio
Un nodo ya creado en Gladys como variador mantiene sus funcionalidades de
variador hasta que no se actualiza desde la pantalla de descubrimiento. La actualización
las reemplaza por la funcionalidad de cable piloto: mismo `external_id` de
dispositivo, nuevo `external_id` de funcionalidad.
- El historial de las antiguas funcionalidades de variador no se recupera. Los
niveles y las órdenes no son los mismos valores.
- Las escenas y los widgets del panel que apuntaban a la antigua
funcionalidad de variador deben volver a apuntar manualmente. La migración de
dispositivo (`device-migration.md`) mueve los selectores de una funcionalidad a
otra, pero un « 30 % » en una escena no significaría una orden.
- Un termostato (virtual, `THERMOSTAT_PILOT_WIRE_FEATURE`) se configura luego
en la nueva funcionalidad en su formulario de edición. Nada más en el lado del
termostato: la correspondencia preset → orden está en la especificación del
termostato, C.1.1.
## 7. Pruebas
- **Descubrimiento:**
- un nodo de `deviceId` `345-81-4` y clase 17-1 publica exactamente una
funcionalidad `heater` / `pilot-wire-mode`, sin posición, estado,
`restorePrevious` ni Binary Switch;
- un nodo de clase 17-1 con **otro** identificador de producto sigue
publicándose como un variador (no regresión).
- **Escritura:** cada una de las seis órdenes produce un `set` CC 38 con su nivel, y
una orden desconocida da un command-result en error, sin enviar nada.
- **Lectura:** los límites de los rangos (0, 10, 11, 20, 21, 30, 31, 40, 41, 50, 51,
99), el 60 observado → `COMFORT`, y 255 / −1 / un valor no numérico no
envían nada.
- **Retorno de estado:** después de una escritura, ningún estado se envía hasta que
`currentValue` no haya llegado.
## 8. Fuera del alcance
- Los parámetros de configuración CC 112 (modos de los botones, estado después de un
corte de energía).
- Otras referencias de Qubino al cable piloto, mientras no se conozca su identificador de
producto y no se confirme que usan los mismos niveles. Cada una se añade
explícitamente a la lista de productos, nunca por la clase de dispositivo.
- Cualquier lógica de regulación: la integración traduce las órdenes, es el
termostato el que las decide.
## 9. Verificación manual
1. Actualizar el nodo « Radiador (Oficina) » desde la pantalla de descubrimiento: aparece
una sola funcionalidad « cable piloto ».
2. Desde el dispositivo, enviar cada orden y verificar el nivel en
zwave-js-ui: apagado 0, protección contra heladas 20, eco 30, confort −2 40, confort −1 50,
confort 99.
3. Presionar el botón del módulo: la orden mostrada en Gladys sigue (60 →
confort).
4. En el formulario del termostato, elegir esta funcionalidad como actuador de cable piloto, luego elegir Eco en el widget: el módulo pasa a 30.
```## 10. Puntos abiertos
- **Bornes de las playas.** Las escrituras (0/20/30/40/50/99) están medidas. Los
rangos de lectura provienen de la documentación de Qubino y aún deben verificarse
en la documentación del ZMNHJD.
- **Numeración confort −1 / −2.** `PILOT_WIRE_MODE` devuelve `COMFORT_1 = 3` y
`COMFORT_2 = 4`. La tabla de la sección 4 sigue la constante, no el orden de los
niveles (40 = confort −2, 50 = confort −1).
