Skip to content

[Bug] ValueError: hour must be in 0-23 range in adapters.py v3.7.0 - Spectra/Magellan parsing #554

@kachytronico

Description

@kachytronico

GitHub Issue Report: PAI Paradox Alarm Interface - ValueError en parsing de fecha/hora

🐛 Descripción del Bug

PAI versión 3.7.0 está generando errores de ValueError al intentar parsear mensajes de eventos en vivo del panel Paradox, específicamente cuando intenta crear objetos datetime con valores de hora inválidos.

📋 Información del Entorno

Sistema Home Assistant

  • Método de instalación: Home Assistant OS
  • Core: 2025.7.1
  • Supervisor: 2025.06.2
  • Operating System: 15.2
  • Frontend: 20250702.1

PAI Configuration

  • Versión PAI: 3.7.0 (actualizado recientemente)
  • Panel: Paradox Spectra/Magellan (basado en el stack trace)
  • Conexión: [NECESITA CONFIRMACIÓN - Serial/IP/Ethernet]

🔥 Error Completo

2025-07-09 16:41:30,494 - ERROR    - PAI.paradox.hardware.spectra_magellan.panel - Exception parsing message: b'e219070910280011000000000000476172616a652031372020202020202000000000001a20'
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/paradox/hardware/spectra_magellan/panel.py", line 173, in parse_message
    return parsers.LiveEvent.parse(message)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 308, in parse
    return self.parse_stream(io.BytesIO(data), **contextkw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 320, in parse_stream
    return self._parsereport(stream, context, "(parsing)")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 2000, in _parse
    subobj = sc._parsereport(stream, context, path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 2489, in _parse
    return self.subcon._parsereport(stream, context, path)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 4368, in _parse
    obj = self.subcon._parsereport(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 2000, in _parse
    subobj = sc._parsereport(stream, context, path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 2489, in _parse
    return self.subcon._parsereport(stream, context, path)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 332, in _parsereport
    obj = self._parse(stream, context, path)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/construct/core.py", line 719, in _parse
    return self._decode(obj, context, path)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/paradox/hardware/spectra_magellan/adapters.py", line 9, in _decode
    return datetime.datetime(obj[0] * 100 + obj[1], obj[2], obj[3], obj[4], obj[5])
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: hour must be in 0..

🔍 Análisis del Error

Mensaje Raw Parseado

b'e219070910280011000000000000476172616a652031372020202020202000000000001a20'

Ubicación del Error

  • Archivo: /usr/local/lib/python3.11/site-packages/paradox/hardware/spectra_magellan/adapters.py
  • Línea: 9
  • Función: _decode()

Problema Identificado

El error ocurre cuando PAI intenta crear un objeto datetime.datetime() en la línea:

return datetime.datetime(obj[0] * 100 + obj[1], obj[2], obj[3], obj[4], obj[5])

Los valores extraídos del mensaje del panel están produciendo un valor de hora (obj[4]) que es ≥ 24, lo cual es inválido para datetime.datetime().

🔄 Reproducibilidad

  • Frecuencia: Ocurre de forma intermitente
  • Desencadenante: Eventos de alarma del panel Paradox
  • Momento: Aparentemente cuando el panel envía eventos en vivo
  • Versión anterior: El problema comenzó después de actualizar a PAI 3.7.0

💡 Posibles Causas

  1. Configuración de hora en panel: El panel podría estar configurado en formato 12h y enviando valores AM/PM que PAI interpreta incorrectamente
  2. Zona horaria: Problemas de conversión de zona horaria
  3. Parsing de formato: Cambios en el protocolo de parsing en v3.7.0
  4. Bug de regresión: Nuevo bug introducido en la versión 3.7.0

🛠️ Solución Temporal Sugerida

Validación en adapters.py

def _decode(self, obj, context, path):
    # Validar y limitar valores antes de crear datetime
    year = obj[0] * 100 + obj[1] if obj[0] < 100 else obj[0]
    month = max(1, min(12, obj[2]))
    day = max(1, min(31, obj[3]))
    hour = max(0, min(23, obj[4]))  # Limitar hora a 0-23
    minute = max(0, min(59, obj[5]))  # Limitar minutos a 0-59
    
    return datetime.datetime(year, month, day, hour, minute)

📊 Información Adicional Necesaria

Para ayudar en el diagnóstico, por favor proporciona:

  1. Modelo exacto del panel Paradox
  2. Configuración de fecha/hora del panel (12h/24h, zona horaria)
  3. Método de conexión (Serial, IP, etc.)
  4. Configuración PAI (archivo de configuración relevante)
  5. ¿Funcionaba correctamente en versión anterior? (¿cuál?)

🎯 Comportamiento Esperado

PAI debería:

  1. Parsear correctamente todos los mensajes de eventos del panel
  2. Manejar graciosamente valores de fecha/hora inválidos
  3. Registrar warnings en lugar de errores fatales para parsing fallido
  4. Continuar operando sin interrupciones

📋 Checklist para Desarrolladores

  • Revisar cambios en adapters.py entre v3.6.x y v3.7.0
  • Añadir validación de valores de fecha/hora antes de crear datetime
  • Implementar logging detallado del parsing de mensajes
  • Considerar modo de compatibilidad para paneles antiguos
  • Añadir tests unitarios para casos de valores extremos

🔗 Enlaces Relevantes

#v3.7.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingto be confirmedReported but not yet confirmed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions