Source code for pylocal_akuvox.models.device

# SPDX-FileCopyrightText: 2026 Andrew Grimberg <tykeal@bardicgrove.org>
# SPDX-License-Identifier: Apache-2.0

"""Device-domain data models (identity, live status, relays)."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from pylocal_akuvox.exceptions import AkuvoxParseError


[docs] @dataclass(frozen=True) class DeviceInfo: """Read-only device identification data.""" model: str mac_address: str firmware_version: str hardware_version: str uptime: str | None = None web_language: int | None = None
[docs] @classmethod def from_api_response(cls, data: dict[str, Any]) -> DeviceInfo: """Create DeviceInfo from API response data.""" status = data.get("Status", {}) if not isinstance(status, dict): msg = "Expected 'Status' to be a dict in device info" raise AkuvoxParseError(msg) web_lang = status.get("WebLang") try: web_language = int(web_lang) if web_lang is not None else None except ValueError: # Separate handlers due to ruff 0.15.0 format bug # that strips parens from `except (ValueError, TypeError):` web_language = None except TypeError: web_language = None try: return cls( model=status["Model"], mac_address=status["MAC"], firmware_version=status["FirmwareVersion"], hardware_version=status["HardwareVersion"], uptime=status.get("Uptime"), web_language=web_language, ) except KeyError as exc: msg = f"Missing required field {exc} in device info" raise AkuvoxParseError(msg) from exc
[docs] @dataclass(frozen=True) class DeviceStatus: """Point-in-time device operational status.""" unix_time: int uptime: int
[docs] @classmethod def from_api_response(cls, data: dict[str, Any]) -> DeviceStatus: """Create DeviceStatus from API response data.""" try: raw_time = data["SystemTime"] raw_uptime = data["UpTime"] except KeyError as exc: msg = f"Missing required field {exc} in device status" raise AkuvoxParseError(msg) from exc try: return cls( unix_time=int(raw_time), uptime=int(raw_uptime), ) except ValueError as exc: # Separate handlers due to ruff 0.15.0 format bug # that strips parens from `except (ValueError, TypeError):` msg = "Invalid type for 'SystemTime' or 'UpTime' in device status" raise AkuvoxParseError(msg) from exc except TypeError as exc: msg = "Invalid type for 'SystemTime' or 'UpTime' in device status" raise AkuvoxParseError(msg) from exc
[docs] @dataclass(frozen=True) class Relay: """Controllable relay on the device.""" number: int state: str | None = None
[docs] @classmethod def from_api_response(cls, data: dict[str, Any]) -> Relay: """Create Relay from API response data.""" try: raw_number = data["number"] except KeyError as exc: msg = f"Missing required field {exc} in relay data" raise AkuvoxParseError(msg) from exc try: number = int(raw_number) except ValueError as exc: # Separate handlers due to ruff 0.15.0 format bug # that strips parens from `except (ValueError, TypeError):` msg = f"Invalid type for relay 'number': got {raw_number!r}" raise AkuvoxParseError(msg) from exc except TypeError as exc: msg = f"Invalid type for relay 'number': got {raw_number!r}" raise AkuvoxParseError(msg) from exc return cls( number=number, state=data.get("state"), )