From 8c22aa53a5fa049f3c000a26107f3fc136fac1c3 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 25 Jun 2024 09:35:19 +0200 Subject: [PATCH] Raise InvalidServerVersion when Server is too old too Currently InvalidServerVersion was only raised when the client was too old (Server wasn't able to handle such an old client anymore), e.g. after a backwards incompatible change Server side. However, if the client was newer than the Server schema version, no error was raised. This essentially never triggered the add-on update logic in Core. This change makes sure that we raise InvalidServerVersion when the Server is too old for the client too. We also add properties to the Exception so a client can figure out which situation we are dealing with (too old/too new) and act accordingly. --- matter_server/client/client.py | 43 +++++++++++++++--------------- matter_server/client/connection.py | 7 +++-- matter_server/client/exceptions.py | 13 +++++++++ 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/matter_server/client/client.py b/matter_server/client/client.py index 6aae7e41..cd285575 100644 --- a/matter_server/client/client.py +++ b/matter_server/client/client.py @@ -10,6 +10,7 @@ from chip.clusters import Objects as Clusters from chip.clusters.Types import NullValue +from matter_server.common.const import SCHEMA_VERSION from matter_server.common.errors import ERROR_MAP, NodeNotExists from ..common.helpers.util import ( @@ -535,14 +536,13 @@ async def update_node( APICommand.UPDATE_NODE, node_id=node_id, software_version=software_version ) - async def send_command( + def _prepare_message( self, command: str, require_schema: int | None = None, **kwargs: Any, - ) -> Any: - """Send a command and get a response.""" - if not self.connection.connected or not self._loop: + ) -> CommandMessage: + if not self.connection.connected: raise InvalidState("Not connected") if ( @@ -552,14 +552,29 @@ async def send_command( ): raise InvalidServerVersion( "Command not available due to incompatible server version. Update the Matter " - f"Server to a version that supports at least api schema {require_schema}." + f"Server to a version that supports at least api schema {require_schema}.", + SCHEMA_VERSION, + self.server_info.schema_version, + self.server_info.min_supported_schema_version, ) - message = CommandMessage( + return CommandMessage( message_id=uuid.uuid4().hex, command=command, args=kwargs, ) + + async def send_command( + self, + command: str, + require_schema: int | None = None, + **kwargs: Any, + ) -> Any: + """Send a command and get a response.""" + if not self._loop: + raise InvalidState("Not connected") + + message = self._prepare_message(command, require_schema, **kwargs) future: asyncio.Future[Any] = self._loop.create_future() self._result_futures[message.message_id] = future await self.connection.send_message(message) @@ -575,22 +590,8 @@ async def send_command_no_wait( **kwargs: Any, ) -> None: """Send a command without waiting for the response.""" - if not self.server_info: - raise InvalidState("Not connected") - if ( - require_schema is not None - and require_schema > self.server_info.schema_version - ): - raise InvalidServerVersion( - "Command not available due to incompatible server version. Update the Matter " - f"Server to a version that supports at least api schema {require_schema}." - ) - message = CommandMessage( - message_id=uuid.uuid4().hex, - command=command, - args=kwargs, - ) + message = self._prepare_message(command, require_schema, **kwargs) await self.connection.send_message(message) async def get_diagnostics(self) -> ServerDiagnostics: diff --git a/matter_server/client/connection.py b/matter_server/client/connection.py index afe78430..b1e4ebe3 100644 --- a/matter_server/client/connection.py +++ b/matter_server/client/connection.py @@ -80,13 +80,16 @@ async def connect(self) -> None: self.server_info = info # basic check for server schema version compatibility - if info.min_supported_schema_version > SCHEMA_VERSION: + if info.min_supported_schema_version > SCHEMA_VERSION > info.schema_version: # our schema version is too low and can't be handled by the server anymore. await self._ws_client.close() raise InvalidServerVersion( f"Matter schema version is incompatible: {SCHEMA_VERSION}, " f"the server requires at least {info.min_supported_schema_version} " - " - update the Matter client to a more recent version or downgrade the server." + " - update the Matter client to a more recent version or downgrade the server.", + SCHEMA_VERSION, + info.schema_version, + info.min_supported_schema_version, ) LOGGER.info( diff --git a/matter_server/client/exceptions.py b/matter_server/client/exceptions.py index 6019ebe0..ddf01226 100644 --- a/matter_server/client/exceptions.py +++ b/matter_server/client/exceptions.py @@ -53,3 +53,16 @@ class InvalidMessage(MatterClientException): class InvalidServerVersion(MatterClientException): """Exception raised when connected to server with incompatible version.""" + + def __init__( + self, + msg: str, + client_schema_version: int, + server_schema_version: int, + min_supported_schema_version: int, + ): + """Initialize an invalid server version error.""" + super().__init__(msg) + self.client_schema_version = client_schema_version + self.server_schema_version = server_schema_version + self.min_supported_schema_version = min_supported_schema_version