Skip to content

Commit

Permalink
Merge pull request #181 from loopj/more-error-types
Browse files Browse the repository at this point in the history
Add support for more specific error types
loopj authored Jan 26, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents d561a81 + 7821c14 commit 1f4ccc4
Showing 2 changed files with 67 additions and 31 deletions.
38 changes: 11 additions & 27 deletions src/aiovantage/command_client/commands.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

import asyncio
import logging
import re
from collections.abc import Sequence
from dataclasses import dataclass
from decimal import Decimal
@@ -11,12 +12,7 @@
from typing_extensions import Self

from aiovantage.connection import BaseConnection
from aiovantage.errors import (
CommandError,
InvalidObjectError,
LoginFailedError,
LoginRequiredError,
)
from aiovantage.errors import CommandError, raise_command_error

from .types import converter, tokenize_response

@@ -142,12 +138,18 @@ async def raw_request(
response_line = await conn.readuntil(b"\r\n", self._read_timeout)
response_line = response_line.rstrip()

# Handle error codes
# Handle command errors
if response_line.startswith("R:ERROR"):
raise self._parse_command_error(response_line)
# Parse a command error from a message.
match = re.match(r"R:ERROR:(\d+) (.+)", response_line)
if not match:
raise CommandError(response_line)

# Convert the error code to a specific exception, if possible
raise_command_error(int(match.group(1)), match.group(2))

# Ignore potentially interleaved "event" messages
if any(response_line.startswith(x) for x in ("S:", "L:", "EL:")):
if response_line.startswith(("S:", "L:", "EL:")):
self._logger.debug("Ignoring event message: %s", response_line)
continue

@@ -183,21 +185,3 @@ async def get_connection(self) -> CommandConnection:
)

return self._connection

def _parse_command_error(self, message: str) -> CommandError:
# Parse a command error from a message.
tag, error_message = message.split(" ", 1)
_, _, error_code_str = tag.split(":")
error_code = int(error_code_str)

exc: CommandError
if error_code == 7:
exc = InvalidObjectError(error_message)
elif error_code == 21:
exc = LoginRequiredError(error_message)
elif error_code == 23:
exc = LoginFailedError(error_message)
else:
exc = CommandError(f"{error_message} (Error code {error_code})")

return exc
60 changes: 56 additions & 4 deletions src/aiovantage/errors.py
Original file line number Diff line number Diff line change
@@ -23,13 +23,65 @@ class CommandError(ClientError):
"""Base exception for errors caused by sending commands or requests."""


class LoginFailedError(CommandError):
"""Login failed."""
class InvalidParameterError(CommandError):
"""An invalid parameter was provided."""


class LoginRequiredError(CommandError):
"""Login is required to perform this command."""
class WrongNumberOfParametersError(CommandError):
"""The wrong number of parameters was provided."""


class SyntaxError(CommandError):
"""The command syntax is invalid."""


class InvalidObjectError(CommandError):
"""The requested object ID is invalid."""


class NotImplementedError(CommandError):
"""The requested command is not implemented."""


class FailedError(CommandError):
"""The requested command failed."""


class NotSupportedError(CommandError):
"""The requested command is not supported."""


class ObjectOfflineError(CommandError):
"""The requested object is offline."""


class LoginRequiredError(CommandError):
"""Login is required to perform this command."""


class LoginFailedError(CommandError):
"""Login failed."""


COMMAND_ERROR_CODES = {
4: InvalidParameterError,
5: WrongNumberOfParametersError,
6: SyntaxError,
7: InvalidObjectError,
8: NotImplementedError,
12: FailedError,
17: NotSupportedError,
20: ObjectOfflineError,
21: LoginRequiredError,
23: LoginFailedError,
}


def raise_command_error(code: int, message: str) -> None:
"""Raise a command error based on the error code."""
error_cls = COMMAND_ERROR_CODES.get(code)

if error_cls is None:
raise CommandError(f"{message} (Error code {code})")

raise error_cls(message)

0 comments on commit 1f4ccc4

Please sign in to comment.