Repository: zpieslak/mobilus-client

Issue:
Change flake8 linter to ruff
Since ruff is faster and provides better configuration options, consider changing flake8.
- Find comunity approved defaults for ruff
- Change workflow to use ruff
- Update README and pyproject.toml

Discussion:

Patch:
diff --git a/.flake8 b/.flake8
deleted file mode 100644
index 6d2e61c..0000000
--- a/.flake8
+++ /dev/null
@@ -1,3 +0,0 @@
-[flake8]
-max-line-length = 120
-exclude = */proto/*
diff --git a/README.md b/README.md
index 53e8931..bc630d4 100644
--- a/README.md
+++ b/README.md
@@ -60,10 +60,10 @@ Install the package in editable mode with the following command:
 
     pip install -e .[test]
 
-Check mypy and flake8 with the following commands:
+Check mypy and ruff with the following commands:
 
     mypy .
-    flake8 .
+    ruff check
 
 The tests can be run with the following command:
 
diff --git a/mobilus_client/__main__.py b/mobilus_client/__main__.py
index a67bb42..ddb488f 100644
--- a/mobilus_client/__main__.py
+++ b/mobilus_client/__main__.py
@@ -1,22 +1,23 @@
 import argparse
+import json
 import logging
 import sys
-from typing import Tuple, Dict
-from mobilus_client.config import Config
+
 from mobilus_client.app import App
+from mobilus_client.config import Config
 
 logger = logging.getLogger(__name__)
 
 
 def main() -> None:
     parser = argparse.ArgumentParser(
-        description="Run Mobilus Client with specific commands and parameters."
+        description="Run Mobilus Client with specific commands and parameters.",
     )
-    parser.add_argument('--host', required=True, help="Cosmo GTW address")
-    parser.add_argument('--login', required=True, help="User login")
-    parser.add_argument('--password', required=True, help="User password")
-    parser.add_argument('--verbose', action='store_true', help="Enable verbose logging")
-    parser.add_argument('commands', nargs='+', help="Commands to run")
+    parser.add_argument("--host", required=True, help="Cosmo GTW address")
+    parser.add_argument("--login", required=True, help="User login")
+    parser.add_argument("--password", required=True, help="User password")
+    parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
+    parser.add_argument("commands", nargs="+", help="Commands to run")
 
     # Parse arguments
     args = parser.parse_args()
@@ -32,7 +33,13 @@ def main() -> None:
     logging.basicConfig(
         stream=sys.stdout,
         level=logging.DEBUG if args.verbose else logging.WARNING,
-        format='{"name": "[%(name)s]", "levelname": "%(levelname)s", "message": "%(message)s"}'
+        format=json.dumps(
+            {
+                "name": "[%(name)s]",
+                "levelname": "%(levelname)s",
+                "message": "%(message)s",
+            },
+        ),
     )
 
     commands = []
@@ -42,19 +49,18 @@ def main() -> None:
 
     app = App(config)
     responses = app.call(commands)
-    print(responses)
+    sys.stdout.write(responses)
 
 
-def _parse_command(command_str: str) -> Tuple[str, Dict[str, str]]:
-    """
-    " Parse a command string into a command and a dictionary of parameters. "
+def _parse_command(command_str: str) -> tuple[str, dict[str, str]]:
+    """" Parse a command string into a command and a dictionary of parameters. "
     " Example: 'command:param1=value1,param2=value2' "
     " Returns: ('command', {'param1': 'value1', 'param2': 'value2'}) "
     """
-    command, *params = command_str.split(':', 1)
-    params_dict = dict(param.split('=') for param in params[0].split(',')) if params else {}
+    command, *params = command_str.split(":", 1)
+    params_dict = dict(param.split("=") for param in params[0].split(",")) if params else {}
     return command, params_dict
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/mobilus_client/app.py b/mobilus_client/app.py
index 462d717..0eae7e0 100644
--- a/mobilus_client/app.py
+++ b/mobilus_client/app.py
@@ -1,11 +1,11 @@
 import logging
 import socket
-from typing import Dict, List, Tuple
+
 from mobilus_client.config import Config
 from mobilus_client.messages.serializer import MessageSerializer
 from mobilus_client.mqtt_client import MqttClient
-from mobilus_client.registries.message import MessageRegistry
 from mobilus_client.registries.key import KeyRegistry
+from mobilus_client.registries.message import MessageRegistry
 
 logger = logging.getLogger(__name__)
 
@@ -22,10 +22,10 @@ class App:
                 "config": config,
                 "key_registry": self.key_registry,
                 "message_registry": self.message_registry,
-            }
+            },
         )
 
-    def call(self, commands: List[Tuple[str, Dict[str, str]]]) -> str:
+    def call(self, commands: list[tuple[str, dict[str, str]]]) -> str:
         if not commands:
             return self._empty_response()
 
@@ -56,7 +56,7 @@ class App:
 
         # Return serialized responses from the message registry
         return MessageSerializer.serialize_list_to_json(
-            self.message_registry.get_responses()
+            self.message_registry.get_responses(),
         )
 
     def _empty_response(self) -> str:
diff --git a/mobilus_client/config.py b/mobilus_client/config.py
index 0d910e2..572ca03 100644
--- a/mobilus_client/config.py
+++ b/mobilus_client/config.py
@@ -1,5 +1,6 @@
-import random
-from dataclasses import dataclass
+import secrets
+from dataclasses import dataclass, field
+
 from mobilus_client.utils.encryption import create_key
 
 
@@ -10,11 +11,11 @@ class Config:
     user_password: str
 
     auth_timeout_period: float = 30
-    client_id: str = ''.join(random.choice('0123456789ABCDEF') for _ in range(12))
+    client_id: str = field(default_factory=lambda: secrets.token_hex(6).upper())
     gateway_port: int = 8884
     gateway_protocol: str = "websockets"
     timeout_period: float = 30
-    user_key: bytes = b''
+    user_key: bytes = b""
 
     def __post_init__(self) -> None:
         self.user_key = create_key(self.user_password)
diff --git a/mobilus_client/messages/encryptor.py b/mobilus_client/messages/encryptor.py
index 64d2c2e..96c4647 100644
--- a/mobilus_client/messages/encryptor.py
+++ b/mobilus_client/messages/encryptor.py
@@ -1,8 +1,11 @@
+from __future__ import annotations
+
 import struct
 import time
+from typing import TYPE_CHECKING, ClassVar, cast
+
 from google.protobuf.message import DecodeError
-from typing import cast, Optional
-from mobilus_client.registries.key import KeyRegistry
+
 from mobilus_client.proto import (
     CallEventsRequest,
     CurrentStateRequest,
@@ -10,14 +13,17 @@ from mobilus_client.proto import (
     DevicesListRequest,
     DevicesListResponse,
     LoginRequest,
-    LoginResponse
+    LoginResponse,
 )
 from mobilus_client.utils.encryption import create_iv, decrypt_body, encrypt_body
 from mobilus_client.utils.types import MessageRequest, MessageResponse
 
+if TYPE_CHECKING:
+    from mobilus_client.registries.key import KeyRegistry
+
 
 class MessageEncryptor:
-    CATEGORY_MAP = {
+    CATEGORY_MAP: ClassVar[dict[int, type[MessageRequest | MessageResponse]]] = {
         1: LoginRequest,
         2: LoginResponse,
         3: DevicesListRequest,
@@ -26,7 +32,9 @@ class MessageEncryptor:
         26: CurrentStateRequest,
         27: CurrentStateResponse,
     }
-    CLASS_TO_CATEGORY_MAP = {v: k for k, v in CATEGORY_MAP.items()}
+    CLASS_TO_CATEGORY_MAP: ClassVar[dict[type[MessageRequest | MessageResponse], int]] = {
+        v: k for k, v in CATEGORY_MAP.items()
+    }
 
     @staticmethod
     def encrypt(message: MessageRequest, client_id: str, key_registry: KeyRegistry) -> bytes:
@@ -44,16 +52,16 @@ class MessageEncryptor:
             encrypted_body = encrypt_body(private_key, iv, body)
 
         return (
-            struct.pack('>IBI', length, category, timestamp) +
+            struct.pack(">IBI", length, category, timestamp) +
             bytes.fromhex(client_id) +
-            struct.pack('>2B', 4, 0) +
+            struct.pack(">2B", 4, 0) +
             encrypted_body
         )
 
     @staticmethod
-    def decrypt(encrypted_message: bytes, key_registry: KeyRegistry) -> Optional[MessageResponse]:
+    def decrypt(encrypted_message: bytes, key_registry: KeyRegistry) -> MessageResponse | None:
         # Define header format and calculate size
-        header_format = '>IBI6sBB'
+        header_format = ">IBI6sBB"
         header_size = struct.calcsize(header_format)
 
         # Do early return if message is too short
@@ -69,9 +77,12 @@ class MessageEncryptor:
 
         # Choose proper klass
         message_klass = MessageEncryptor.CATEGORY_MAP.get(category)
+
         if message_klass is None:
             return None
 
+        message_klass = cast(type[MessageResponse], message_klass)
+
         # Choose proper decryption key
         key = key_registry.get_decryption_key(message_klass)
 
@@ -80,7 +91,7 @@ class MessageEncryptor:
         body = decrypt_body(key, iv, encrypted_body)
 
         try:
-            message = cast(MessageResponse, message_klass())
+            message = message_klass()
             message.ParseFromString(body)
         except DecodeError:
             return None
diff --git a/mobilus_client/messages/factory.py b/mobilus_client/messages/factory.py
index f94ca0c..39cecee 100644
--- a/mobilus_client/messages/factory.py
+++ b/mobilus_client/messages/factory.py
@@ -1,5 +1,7 @@
-from abc import ABC, abstractmethod
-from typing import Any, Optional
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, ClassVar
+
 from mobilus_client.proto import (
     CallEvent,
     CallEventsRequest,
@@ -7,53 +9,51 @@ from mobilus_client.proto import (
     DevicesListRequest,
     LoginRequest,
 )
-from mobilus_client.utils.types import MessageRequest
-
 
-class MessageBuilder(ABC):
-    @abstractmethod
-    def build(self, **kwargs: Any) -> Optional[MessageRequest]:
-        pass
+if TYPE_CHECKING:
+    from mobilus_client.utils.types import MessageRequest
 
 
-class LoginMessageBuilder(MessageBuilder):
-    def build(self, **kwargs: Any) -> Optional[LoginRequest]:
-        if 'login' not in kwargs or 'password' not in kwargs:
+class CallEventsMessageBuilder:
+    def build(self, device_id: str = "", value: str = "", event_number: str = "6") -> CallEventsRequest | None:
+        if not device_id or not value:
             return None
 
-        request = LoginRequest()
-        request.login = str(kwargs['login'])
-        request.password = bytes(kwargs['password'])
+        event = CallEvent()
+        event.event_number = int(event_number)
+        event.device_id = int(device_id)
+        event.value = value
+
+        request = CallEventsRequest()
+        request.events.append(event)
         return request
 
 
-class DevicesListMessageBuilder(MessageBuilder):
-    def build(self, **kwargs: Any) -> DevicesListRequest:
-        return DevicesListRequest()
+class CurrentStateMessageBuilder:
+    def build(self) -> CurrentStateRequest:
+        return CurrentStateRequest()
 
 
-class CurrentStateMessageBuilder(MessageBuilder):
-    def build(self, **kwargs: Any) -> CurrentStateRequest:
-        return CurrentStateRequest()
+class DevicesListMessageBuilder:
+    def build(self) -> DevicesListRequest:
+        return DevicesListRequest()
 
 
-class CallEventsMessageBuilder(MessageBuilder):
-    def build(self, **kwargs: Any) -> Optional[CallEventsRequest]:
-        if 'device_id' not in kwargs or 'value' not in kwargs:
+class LoginMessageBuilder:
+    def build(self, login: str = "", password: bytes = b"") -> LoginRequest | None:
+        if not login or not password:
             return None
 
-        event = CallEvent()
-        event.event_number = int(kwargs.get('event_number', 6))
-        event.device_id = int(kwargs['device_id'])
-        event.value = str(kwargs['value'])
-
-        request = CallEventsRequest()
-        request.events.append(event)
+        request = LoginRequest()
+        request.login = login
+        request.password = password
         return request
 
 
 class MessageFactory:
-    _builders = {
+    _builders: ClassVar[
+      dict[str, LoginMessageBuilder | DevicesListMessageBuilder | CurrentStateMessageBuilder | CallEventsMessageBuilder]
+     ] = {
         "call_events": CallEventsMessageBuilder(),
         "current_state": CurrentStateMessageBuilder(),
         "devices_list": DevicesListMessageBuilder(),
@@ -61,6 +61,6 @@ class MessageFactory:
     }
 
     @staticmethod
-    def create_message(message_name: str, **kwargs: Any) -> Optional[MessageRequest]:
+    def create_message(message_name: str, **kwargs: Any) -> MessageRequest | None: # noqa: ANN401
         builder = MessageFactory._builders.get(message_name)
         return builder.build(**kwargs) if builder else None
diff --git a/mobilus_client/messages/serializer.py b/mobilus_client/messages/serializer.py
index 66decc5..e48441a 100644
--- a/mobilus_client/messages/serializer.py
+++ b/mobilus_client/messages/serializer.py
@@ -1,5 +1,7 @@
 import json
+
 from google.protobuf.json_format import MessageToJson
+
 from mobilus_client.utils.types import MessageResponse
 
 
@@ -13,8 +15,8 @@ class MessageSerializer:
         return json.dumps(
             [
                 json.loads(
-                    MessageSerializer.serialize_to_json(message)
+                    MessageSerializer.serialize_to_json(message),
                 )
                 for message in messages
-            ]
+            ],
         )
diff --git a/mobilus_client/messages/validator.py b/mobilus_client/messages/validator.py
index 22cb580..f3d1ae5 100644
--- a/mobilus_client/messages/validator.py
+++ b/mobilus_client/messages/validator.py
@@ -1,12 +1,17 @@
-from typing import Optional, Union
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
 from mobilus_client.messages.status import MessageStatus
 from mobilus_client.proto import LoginResponse
-from mobilus_client.utils.types import MessageRequest, MessageResponse
+
+if TYPE_CHECKING:
+    from mobilus_client.utils.types import MessageRequest, MessageResponse
 
 
-class MessageValidator():
+class MessageValidator:
     @staticmethod
-    def validate(message: Optional[Union[MessageRequest, MessageResponse]]) -> MessageStatus:
+    def validate(message: MessageRequest | MessageResponse | None) -> MessageStatus:
         if message is None:
             return MessageStatus.UNKNOWN_MESSAGE
 
diff --git a/mobilus_client/mqtt_client.py b/mobilus_client/mqtt_client.py
index d12c749..d935d37 100644
--- a/mobilus_client/mqtt_client.py
+++ b/mobilus_client/mqtt_client.py
@@ -1,81 +1,84 @@
+from __future__ import annotations
+
 import logging
-import paho.mqtt.client as mqtt
 import threading
-from typing import Any, Dict, cast
+from typing import Any, cast
+
+import paho.mqtt.client as mqtt
+
 from mobilus_client.messages.encryptor import MessageEncryptor
 from mobilus_client.messages.factory import MessageFactory
 from mobilus_client.messages.status import MessageStatus
 from mobilus_client.messages.validator import MessageValidator
-from mobilus_client.proto import (LoginRequest, LoginResponse)
+from mobilus_client.proto import LoginRequest, LoginResponse
 from mobilus_client.utils.types import MessageRequest
 
-
 logger = logging.getLogger(__name__)
 
 
 class MqttClient(mqtt.Client):
-    _userdata: Dict[str, Any]
+    _userdata: dict[str, Any]
 
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
+    def __init__(self, **kwargs: Any) -> None: # noqa: ANN401
+        super().__init__(**kwargs)
         self.authenticated_event = threading.Event()
         self.completed_event = threading.Event()
         self.enable_logger(logger)
 
-    def send_request(self, command: str, **params: Any) -> None:
+    def send_request(self, command: str, **params: str | bytes | int | None) -> None:
         if not self.is_connected():
-            logger.error(f"Sending request - {command} failed. Client is not connected.")
+            logger.error("Sending request - %s failed. Client is not connected.", command)
             return
 
         message = MessageFactory.create_message(command, **params)
         status = MessageValidator.validate(message)
 
         if status != MessageStatus.SUCCESS:
-            logger.error(f"Command - {command} returned an error - {status.name}")
+            logger.error("Command - %s returned an error - %s", command, status.name)
             self.disconnect()
             return
 
         if not isinstance(message, LoginRequest):
-            self._userdata['message_registry'].register_request(message)
+            self._userdata["message_registry"].register_request(message)
 
         encrypted_message = MessageEncryptor.encrypt(
             cast(MessageRequest, message),
             self._userdata["config"].client_id,
-            self._userdata['key_registry']
+            self._userdata["key_registry"],
         )
 
         self.publish("module", encrypted_message)
 
-    def on_disconnect(self, client: mqtt.Client, userdata: Any, reason_code: Any) -> None:  # type: ignore
-        logger.info(f"Disconnected with result code - {reason_code}")
+    def on_disconnect(self, _client: mqtt.Client, _userdata: dict[str, Any], reason_code: int) -> None:  # type: ignore[override]
+        logger.info("Disconnected with result code - %s", reason_code)
 
-    def on_connect(self, client: mqtt.Client, userdata: Any, flags: Any, reason_code: Any) -> None:  # type: ignore
+    def on_connect(self, client: mqtt.Client, userdata: dict[str, Any], *_args: Any) -> None:  # type: ignore[override] # noqa: ANN401
         client.subscribe([
             (userdata["config"].client_id, 0),
-            ("clients", 0)
+            ("clients", 0),
         ])
 
-    def on_subscribe(self, client: mqtt.Client, userdata: Any, mid: Any, granted_qos: Any) -> None:  # type: ignore
+    def on_subscribe(self, _client: mqtt.Client, userdata: dict[str, Any], *_args: Any) -> None:  # type: ignore[override]  # noqa: ANN401
         self.send_request(
             "login",
             login=userdata["config"].user_login,
-            password=userdata["config"].user_key
+            password=userdata["config"].user_key,
         )
 
-    def on_message(self, client: mqtt.Client, userdata: Any, message: Any) -> None:  # type: ignore
-        logger.info(f"Received message on topic - {message.topic}")
+    def on_message(self, _client: mqtt.Client, userdata: dict[str, Any], mqtt_message: mqtt.MQTTMessage) -> None:  # type: ignore[override]
+        logger.info("Received message on topic - %s", mqtt_message.topic)
 
-        message = MessageEncryptor.decrypt(message.payload, userdata["key_registry"])
-        logger.info(f"Decrypted message - {type(message).__name__}")
+        message = MessageEncryptor.decrypt(mqtt_message.payload, userdata["key_registry"])
+        logger.info("Decrypted message - %s", type(message).__name__)
 
         status = MessageValidator.validate(message)
 
         if status != MessageStatus.SUCCESS:
-            logger.error(f"Message - {type(message).__name__} returned an error - {status.name}")
+            logger.error("Message - %s returned an error - %s", type(message).__name__, status.name)
             self.disconnect()
             return
 
-        logger.info(f"Message - {type(message).__name__} validated successfully")
+        logger.info("Message - %s validated successfully", type(message).__name__)
 
         if isinstance(message, LoginResponse):
             userdata["key_registry"].register_keys(message)
diff --git a/mobilus_client/registries/__init__.py b/mobilus_client/registries/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mobilus_client/registries/key.py b/mobilus_client/registries/key.py
index d9c1642..fe04543 100644
--- a/mobilus_client/registries/key.py
+++ b/mobilus_client/registries/key.py
@@ -1,9 +1,11 @@
-from typing import Dict, Optional, Type
-from mobilus_client.proto import (
-    CallEventsRequest,
-    LoginResponse
-)
-from mobilus_client.utils.types import MessageRequest, MessageResponse
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from mobilus_client.proto import CallEventsRequest, LoginResponse
+
+if TYPE_CHECKING:
+    from mobilus_client.utils.types import MessageRequest, MessageResponse
 
 
 class KeyRegistry:
@@ -12,22 +14,24 @@ class KeyRegistry:
             "user_key": user_key,
         }
 
-    def get_keys(self) -> Dict[str, bytes]:
+    def get_keys(self) -> dict[str, bytes]:
         return self._registry
 
     def register_keys(self, message: LoginResponse) -> None:
         self._registry["private_key"] = message.private_key
         self._registry["public_key"] = message.public_key
 
-    def get_decryption_key(self, message_klass: Type[MessageResponse]) -> bytes:
+    def get_decryption_key(self, message_klass: type[MessageResponse]) -> bytes:
         if message_klass == LoginResponse:
             return self._registry["user_key"]
-        elif message_klass == CallEventsRequest:
+
+        if message_klass == CallEventsRequest:
             return self._registry["public_key"]
-        else:
-            return self._registry["private_key"]
 
-    def get_encryption_key(self, message_klass: Type[MessageRequest]) -> Optional[bytes]:
+        return self._registry["private_key"]
+
+    def get_encryption_key(self, message_klass: type[MessageRequest]) -> bytes | None:
         if message_klass == CallEventsRequest:
             return self._registry.get("private_key")
+
         return None
diff --git a/mobilus_client/registries/message.py b/mobilus_client/registries/message.py
index 82d0ff5..b5b7828 100644
--- a/mobilus_client/registries/message.py
+++ b/mobilus_client/registries/message.py
@@ -1,5 +1,6 @@
 from collections import Counter
-from typing import List
+from typing import ClassVar
+
 from mobilus_client.proto import (
     CallEventsRequest,
     CurrentStateRequest,
@@ -7,22 +8,22 @@ from mobilus_client.proto import (
     DevicesListRequest,
     DevicesListResponse,
     LoginRequest,
-    LoginResponse
+    LoginResponse,
 )
 from mobilus_client.utils.types import MessageRequest, MessageResponse
 
 
 class MessageRegistry:
-    MESSAGE_MAP = {
+    MESSAGE_MAP: ClassVar[dict[type[MessageRequest], type[MessageResponse]]] = {
         CallEventsRequest: CallEventsRequest,
         CurrentStateRequest: CurrentStateResponse,
         DevicesListRequest: DevicesListResponse,
-        LoginRequest: LoginResponse
+        LoginRequest: LoginResponse,
     }
 
     def __init__(self) -> None:
-        self._requests: List[MessageRequest] = []
-        self._responses: List[MessageResponse] = []
+        self._requests: list[MessageRequest] = []
+        self._responses: list[MessageResponse] = []
 
     def register_request(self, message_request: MessageRequest) -> None:
         self._requests.append(message_request)
@@ -30,10 +31,10 @@ class MessageRegistry:
     def register_response(self, message_response: MessageResponse) -> None:
         self._responses.append(message_response)
 
-    def get_requests(self) -> List[MessageRequest]:
+    def get_requests(self) -> list[MessageRequest]:
         return self._requests
 
-    def get_responses(self) -> List[MessageResponse]:
+    def get_responses(self) -> list[MessageResponse]:
         return self._responses
 
     def all_responses_received(self) -> bool:
diff --git a/mobilus_client/utils/__init__.py b/mobilus_client/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mobilus_client/utils/encryption.py b/mobilus_client/utils/encryption.py
index 5f13c25..66e500f 100644
--- a/mobilus_client/utils/encryption.py
+++ b/mobilus_client/utils/encryption.py
@@ -1,14 +1,15 @@
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 from hashlib import sha256
 from struct import pack
 
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+
 
 def create_iv(value: int) -> bytearray:
-    return bytearray(b'\x00' * 12) + pack('>I', value)
+    return bytearray(b"\x00" * 12) + pack(">I", value)
 
 
 def create_key(value: str) -> bytes:
-    return sha256(value.encode('utf-8')).digest()
+    return sha256(value.encode("utf-8")).digest()
 
 
 def decrypt_body(key: bytes, iv: bytes, value: bytes) -> bytes:
diff --git a/mobilus_client/utils/types.py b/mobilus_client/utils/types.py
index b95cddb..cccaf89 100644
--- a/mobilus_client/utils/types.py
+++ b/mobilus_client/utils/types.py
@@ -1,4 +1,5 @@
 from typing import Union
+
 from mobilus_client.proto import (
     CallEventsRequest,
     CurrentStateRequest,
@@ -6,18 +7,18 @@ from mobilus_client.proto import (
     DevicesListRequest,
     DevicesListResponse,
     LoginRequest,
-    LoginResponse
+    LoginResponse,
 )
 
 MessageRequest = Union[
     CallEventsRequest,
     CurrentStateRequest,
     DevicesListRequest,
-    LoginRequest
+    LoginRequest,
 ]
 MessageResponse = Union[
     CallEventsRequest,
     CurrentStateResponse,
     DevicesListResponse,
-    LoginResponse
+    LoginResponse,
 ]
diff --git a/pyproject.toml b/pyproject.toml
index b32fee7..fe88170 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,39 +7,50 @@ name = "mobilus-client"
 dynamic = ["version"]
 description = "Python client for Mobilus Cosmo GTW"
 readme = "README.md"
-license = { file = "LICENSE" }
-authors = [{ name = "Zbigniew Pieślak", email = "zpieslak@gmail.com" }]
+license = {file = "LICENSE"}
+authors = [{name = "Zbigniew Pieślak", email = "zpieslak@gmail.com"}]
 dependencies = [
-    "paho-mqtt~=1.6.0",
-    "cryptography~=43.0",
-    "protobuf==5.28.1",
+  "paho-mqtt~=1.6.0",
+  "cryptography~=43.0",
+  "protobuf==5.28.1"
 ]
 requires-python = ">=3.9"
 
 [project.optional-dependencies]
 test = [
-    "coverage~=7.0",
-    "factory-boy~=2.0",
-    "flake8~=7.0",
-    "mypy~=1.0",
-    "mypy-protobuf~=3.0",
-    "types-paho-mqtt~=1.0",
-    "types-protobuf~=5.28.0",
+  "coverage~=7.0",
+  "factory-boy~=2.0",
+  "mypy~=1.0",
+  "mypy-protobuf~=3.0",
+  "ruff~=0.6.0",
+  "types-paho-mqtt~=1.0",
+  "types-protobuf~=5.28.0"
 ]
 
 [project.urls]
 homepage = "https://github.com/zpieslak/mobilus-client"
 issues = "https://github.com/zpieslak/mobilus-client/issues"
 
-[tool.hatch.version]
-source = "vcs"
+[tool.coverage.report]
+exclude_also = [
+  "if TYPE_CHECKING:"
+]
+
+[tool.coverage.run]
+omit = [
+  "*/proto/*",
+  "tests/*"
+]
 
 [tool.hatch.build]
 exclude = [
-    ".*",
-    "tests/*",
+  ".*",
+  "tests/*"
 ]
 
+[tool.hatch.version]
+source = "vcs"
+
 [tool.mypy]
 strict = true
 
@@ -47,13 +58,13 @@ strict = true
 module = "factory"
 ignore_missing_imports = true
 
-[tool.coverage.run]
-omit = [
-    "*/proto/*",
-    "tests/*",
-]
+[tool.ruff]
+exclude = ["*/proto/*"]
+line-length = 120
 
-[tool.coverage.report]
-exclude_lines = [
-    "@abstractmethod",
-]
+[tool.ruff.lint]
+ignore = ["D", "PT", "TRY400"]
+select = ["ALL"]
+
+[tool.ruff.lint.per-file-ignores]
+"tests/*" = ["S106"]


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 362736d..7b9a089 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,8 +22,8 @@ jobs:
           python-version: ${{ matrix.python-version }}
       - name: Install dependencies
         run: pip install -e ".[test]"
-      - name: Lint with flake8
-        run: flake8 . --count --statistics
+      - name: Lint with ruff
+        run: ruff check --output-format=github .
       - name: Lint with mypy
         run: mypy .
       - name: Test
diff --git a/tests/factories.py b/tests/factories.py
index 87228e5..7eaebde 100644
--- a/tests/factories.py
+++ b/tests/factories.py
@@ -1,16 +1,18 @@
+from __future__ import annotations
+
 import factory
-from typing import Any, Optional, Dict
+
 from mobilus_client.proto import (
     CallEvent,
     CallEventsRequest,
+    CurrentStateEvent,
     CurrentStateRequest,
     CurrentStateResponse,
-    CurrentStateEvent,
     Device,
     DevicesListRequest,
     DevicesListResponse,
+    LoginRequest,
     LoginResponse,
-    LoginRequest
 )
 
 
@@ -22,8 +24,8 @@ class LoginRequestFactory(BaseFactory):
     class Meta:
         model = LoginRequest
 
-    login = factory.Faker('word')
-    password = factory.Faker('binary', length=16)
+    login = factory.Faker("word")
+    password = factory.Faker("binary", length=16)
 
 
 class LoginResponseFactory(BaseFactory):
@@ -31,11 +33,11 @@ class LoginResponseFactory(BaseFactory):
         model = LoginResponse
 
     login_status = 0
-    private_key = factory.Faker('binary', length=16)
-    public_key = factory.Faker('binary', length=16)
-    admin = factory.Faker('boolean')
-    serial_number = factory.Faker('word')
-    user_id = factory.Faker('random_int', min=1, max=100)
+    private_key = factory.Faker("binary", length=16)
+    public_key = factory.Faker("binary", length=16)
+    admin = factory.Faker("boolean")
+    serial_number = factory.Faker("word")
+    user_id = factory.Faker("random_int", min=1, max=100)
 
     class Params:
         failed = factory.Trait(
@@ -52,9 +54,9 @@ class CurrentStateEventFactory(BaseFactory):
     class Meta:
         model = CurrentStateEvent
 
-    device_id: int = factory.Faker('random_int', min=1, max=100)
-    event_number: int = factory.Faker('random_int', min=1, max=100)
-    value: str = factory.Faker('word')
+    device_id: int = factory.Faker("random_int", min=1, max=100)
+    event_number: int = factory.Faker("random_int", min=1, max=100)
+    value: str = factory.Faker("word")
 
 
 class CurrentStateResponseFactory(BaseFactory):
@@ -62,14 +64,14 @@ class CurrentStateResponseFactory(BaseFactory):
         model = CurrentStateResponse
 
     @factory.post_generation  # type: ignore[misc]
-    def event(obj: CurrentStateResponse, create: bool, extracted: Optional[Dict[str, Any]], **kwargs: Any) -> None:
+    def event(self, _create: bool | None, extracted: dict[str, str | int] | None) -> None:
         event = CurrentStateEventFactory()
 
         if isinstance(extracted, dict):
             for key, value in extracted.items():
                 setattr(event, key, value)
 
-        obj.events.append(event)
+        self.events.append(event)
 
 
 class DevicesListRequestFactory(BaseFactory):
@@ -81,12 +83,12 @@ class DeviceFactory(BaseFactory):
     class Meta:
         model = Device
 
-    id: int = factory.Faker('random_int', min=1, max=100)
-    name: str = factory.Faker('word')
-    type: int = factory.Faker('random_int', min=1, max=100)
-    icon: int = factory.Faker('random_int', min=1, max=100)
-    inserttime: int = factory.Faker('random_int', min=1, max=100)
-    favourite: bool = factory.Faker('boolean')
+    id: int = factory.Faker("random_int", min=1, max=100)
+    name: str = factory.Faker("word")
+    type: int = factory.Faker("random_int", min=1, max=100)
+    icon: int = factory.Faker("random_int", min=1, max=100)
+    inserttime: int = factory.Faker("random_int", min=1, max=100)
+    favourite: bool = factory.Faker("boolean")
 
 
 class DevicesListResponseFactory(BaseFactory):
@@ -94,7 +96,7 @@ class DevicesListResponseFactory(BaseFactory):
         model = DevicesListResponse
 
     @factory.post_generation  # type: ignore[misc]
-    def devices(obj: DevicesListResponse, create: bool, extracted: Optional[Dict[str, Any]], **kwargs: Any) -> None:
+    def devices(self, _create: bool | None, extracted: dict[str, str | int | bytes] | None) -> None:
         device = DeviceFactory()
 
         if isinstance(extracted, list):
@@ -102,17 +104,17 @@ class DevicesListResponseFactory(BaseFactory):
                 for key, value in item.items():
                     setattr(device, key, value)
 
-                obj.devices.append(device)
+                self.devices.append(device)
 
 
 class CallEventFactory(BaseFactory):
     class Meta:
         model = CallEvent
 
-    device_id: int = factory.Faker('random_int', min=1, max=100)
-    event_number: int = factory.Faker('random_int', min=1, max=100)
-    value: str = factory.Faker('word')
-    platform: int = factory.Faker('random_int', min=1, max=100)
+    device_id: int = factory.Faker("random_int", min=1, max=100)
+    event_number: int = factory.Faker("random_int", min=1, max=100)
+    value: str = factory.Faker("word")
+    platform: int = factory.Faker("random_int", min=1, max=100)
 
 
 class CallEventsRequestFactory(BaseFactory):
@@ -120,11 +122,11 @@ class CallEventsRequestFactory(BaseFactory):
         model = CallEventsRequest
 
     @factory.post_generation  # type: ignore[misc]
-    def event(obj: CallEventsRequest, create: bool, extracted: Optional[Dict[str, Any]], **kwargs: Any) -> None:
+    def event(self, _create: bool | None, extracted: dict[str, str | int] | None) -> None:
         event = CallEventFactory()
 
         if isinstance(extracted, dict):
             for key, value in extracted.items():
                 setattr(event, key, value)
 
-        obj.events.append(event)
+        self.events.append(event)
diff --git a/tests/helpers.py b/tests/helpers.py
index b4e46db..2de7740 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -1,5 +1,6 @@
 import struct
 import time
+
 from mobilus_client.messages.encryptor import MessageEncryptor
 from mobilus_client.utils.encryption import create_iv, encrypt_body
 from mobilus_client.utils.types import MessageResponse
@@ -24,6 +25,6 @@ def encrypt_message(message: MessageResponse, key: bytes) -> bytes:
     encrypted_body = encrypt_body(key, iv, body)
 
     return (
-        struct.pack('>IBI6sBB', length, category, timestamp, user_id, platform, response_code) +
+        struct.pack(">IBI6sBB", length, category, timestamp, user_id, platform, response_code) +
         encrypted_body
     )
diff --git a/tests/messages/test_encryptor.py b/tests/messages/test_encryptor.py
index 0f84578..bba0770 100644
--- a/tests/messages/test_encryptor.py
+++ b/tests/messages/test_encryptor.py
@@ -1,8 +1,8 @@
 import unittest
-from unittest.mock import patch, Mock
+from unittest.mock import Mock, patch
+
 from mobilus_client.messages.encryptor import MessageEncryptor
 from mobilus_client.registries.key import KeyRegistry
-from tests.helpers import encrypt_message
 from tests.factories import (
     CallEventsRequestFactory,
     CurrentStateRequestFactory,
@@ -12,22 +12,23 @@ from tests.factories import (
     LoginRequestFactory,
     LoginResponseFactory,
 )
+from tests.helpers import encrypt_message
 
 
 class TestMessageEncryptor(unittest.TestCase):
     def setUp(self) -> None:
         self.client_id = "0123456789ABCDEF"
-        self.user_key = b'test_user_key___'
-        self.private_key = b'test_private_key'
-        self.public_key = b'test_public_key_'
+        self.user_key = b"test_user_key___"
+        self.private_key = b"test_private_key"
+        self.public_key = b"test_public_key_"
         self.login_response = LoginResponseFactory(
             private_key=self.private_key,
-            public_key=self.public_key
+            public_key=self.public_key,
         )
         self.key_registry = KeyRegistry(self.user_key)
         self.key_registry.register_keys(self.login_response)
 
-    @patch('time.time', return_value=1633036800, autospec=True)
+    @patch("time.time", return_value=1633036800, autospec=True)
     def test_encrypt_login_request(self, _mock_time: Mock) -> None:
         request = LoginRequestFactory(login="username", password=self.user_key)
 
@@ -35,10 +36,10 @@ class TestMessageEncryptor(unittest.TestCase):
 
         self.assertEqual(
             result,
-            b'\x00\x00\x00\r\x01aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00\n\x08username\x12\x10test_user_key___'
+            b"\x00\x00\x00\r\x01aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00\n\x08username\x12\x10test_user_key___",
         )
 
-    @patch('time.time', return_value=1633036800, autospec=True)
+    @patch("time.time", return_value=1633036800, autospec=True)
     def test_encrypt_devices_list_request(self, _mock_time: Mock) -> None:
         request = DevicesListRequestFactory()
 
@@ -46,10 +47,10 @@ class TestMessageEncryptor(unittest.TestCase):
 
         self.assertEqual(
             result,
-            b'\x00\x00\x00\r\x03aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00'
+            b"\x00\x00\x00\r\x03aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00",
         )
 
-    @patch('time.time', return_value=1633036800, autospec=True)
+    @patch("time.time", return_value=1633036800, autospec=True)
     def test_encrypt_current_state_request(self, _mock_time: Mock) -> None:
         request = CurrentStateRequestFactory()
 
@@ -57,35 +58,35 @@ class TestMessageEncryptor(unittest.TestCase):
 
         self.assertEqual(
             result,
-            b'\x00\x00\x00\r\x1aaV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00'
+            b"\x00\x00\x00\r\x1aaV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00",
         )
 
-    @patch('time.time', return_value=1633036800, autospec=True)
+    @patch("time.time", return_value=1633036800, autospec=True)
     def test_encrypt_call_events_request(self, _mock_time: Mock) -> None:
         request = CallEventsRequestFactory(
             event={
-                'device_id': 1,
-                'event_number': 1,
-                'value': 'value',
-                'platform': 1
-            }
+                "device_id": 1,
+                "event_number": 1,
+                "value": "value",
+                "platform": 1,
+            },
         )
 
         result = MessageEncryptor.encrypt(request, self.client_id, self.key_registry)
 
         self.assertEqual(
             result,
-            b'\x00\x00\x00\r\raV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x003\xc4\x06\xb4\x9f!\x1b\xfcj2\xd2_\xd9\xe0\x94'
+            b"\x00\x00\x00\r\raV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x003\xc4\x06\xb4\x9f!\x1b\xfcj2\xd2_\xd9\xe0\x94",
         )
 
     def test_decrypt_login_response(self) -> None:
         message = LoginResponseFactory(
             login_status=0,
-            private_key=b'test_private_key',
-            public_key=b'test_public_key_',
+            private_key=b"test_private_key",
+            public_key=b"test_public_key_",
             admin=True,
-            serial_number='serial_number',
-            user_id=1
+            serial_number="serial_number",
+            user_id=1,
         )
         encrypted_message = encrypt_message(message, self.user_key)
 
@@ -97,14 +98,14 @@ class TestMessageEncryptor(unittest.TestCase):
         message = DevicesListResponseFactory(
             devices=[
                 {
-                    'id': 1,
-                    'name': 'name',
-                    'type': 1,
-                    'icon': 1,
-                    'inserttime': 1,
-                    'favourite': True,
-                }
-            ]
+                    "id": 1,
+                    "name": "name",
+                    "type": 1,
+                    "icon": 1,
+                    "inserttime": 1,
+                    "favourite": True,
+                },
+            ],
         )
         encrypted_message = encrypt_message(message, self.private_key)
 
@@ -115,10 +116,10 @@ class TestMessageEncryptor(unittest.TestCase):
     def test_decrypt_current_state_response(self) -> None:
         message = CurrentStateResponseFactory(
             event={
-                'device_id': 1,
-                'event_number': 1,
-                'value': 'value',
-            }
+                "device_id": 1,
+                "event_number": 1,
+                "value": "value",
+            },
         )
         encrypted_message = encrypt_message(message, self.private_key)
 
@@ -129,11 +130,11 @@ class TestMessageEncryptor(unittest.TestCase):
     def test_decrypt_call_events_response(self) -> None:
         message = CallEventsRequestFactory(
             event={
-                'device_id': 1,
-                'event_number': 1,
-                'value': 'value',
-                'platform': 1
-            }
+                "device_id": 1,
+                "event_number": 1,
+                "value": "value",
+                "platform": 1,
+            },
         )
         encrypted_message = encrypt_message(message, self.public_key)
 
@@ -142,14 +143,14 @@ class TestMessageEncryptor(unittest.TestCase):
         self.assertEqual(result, message)
 
     def test_decrypt_invalid_message_with_short_message(self) -> None:
-        encrypted_message = b'invalid'
+        encrypted_message = b"invalid"
 
         result = MessageEncryptor.decrypt(encrypted_message, self.key_registry)
 
         self.assertIsNone(result)
 
     def test_decrypt_invalid_message_with_long_message(self) -> None:
-        encrypted_message = b'invalid_message__'
+        encrypted_message = b"invalid_message__"
 
         result = MessageEncryptor.decrypt(encrypted_message, self.key_registry)
 
diff --git a/tests/messages/test_factory.py b/tests/messages/test_factory.py
index 80ec6ad..141f190 100644
--- a/tests/messages/test_factory.py
+++ b/tests/messages/test_factory.py
@@ -1,4 +1,5 @@
 import unittest
+
 from mobilus_client.messages.factory import MessageFactory
 from mobilus_client.proto import (
     CallEventsRequest,
diff --git a/tests/messages/test_serializer.py b/tests/messages/test_serializer.py
index 8ccc0b9..4221695 100644
--- a/tests/messages/test_serializer.py
+++ b/tests/messages/test_serializer.py
@@ -1,5 +1,6 @@
 import json
 import unittest
+
 from mobilus_client.messages.serializer import MessageSerializer
 from tests.factories import (
     CallEventsRequestFactory,
@@ -19,23 +20,23 @@ class TestMessageSerializer(unittest.TestCase):
                     "icon": 3,
                     "inserttime": 4,
                     "favourite": True,
-                }
-            ]
+                },
+            ],
         )
 
         result = MessageSerializer.serialize_to_json(request)
 
         self.assertEqual(json.loads(result), {
-            'devices': [
+            "devices": [
                 {
-                    'id': "1",
-                    'name': 'device_name',
-                    'type': 2,
-                    'icon': 3,
-                    'inserttime': "4",
-                    'favourite': True,
-                }
-            ]
+                    "id": "1",
+                    "name": "device_name",
+                    "type": 2,
+                    "icon": 3,
+                    "inserttime": "4",
+                    "favourite": True,
+                },
+            ],
         })
 
     def test_serialize_current_state_response_to_json(self) -> None:
@@ -44,19 +45,19 @@ class TestMessageSerializer(unittest.TestCase):
                 "device_id": 1,
                 "event_number": 7,
                 "value": "value",
-            }
+            },
         )
 
         result = MessageSerializer.serialize_to_json(request)
 
         self.assertEqual(json.loads(result), {
-            'events': [
+            "events": [
                 {
-                    'deviceId': '1',
-                    'eventNumber': 7,
-                    'value': 'value',
-                }
-            ]
+                    "deviceId": "1",
+                    "eventNumber": 7,
+                    "value": "value",
+                },
+            ],
         })
 
     def test_serialize_call_events_request_to_json(self) -> None:
@@ -65,21 +66,21 @@ class TestMessageSerializer(unittest.TestCase):
                 "device_id": 1,
                 "event_number": 7,
                 "value": "value",
-                "platform": 1
-            }
+                "platform": 1,
+            },
         )
 
         result = MessageSerializer.serialize_to_json(request)
 
         self.assertEqual(json.loads(result), {
-            'events': [
+            "events": [
                 {
-                    'deviceId': '1',
-                    'eventNumber': 7,
-                    'value': 'value',
-                    'platform': 1
-                }
-            ]
+                    "deviceId": "1",
+                    "eventNumber": 7,
+                    "value": "value",
+                    "platform": 1,
+                },
+            ],
         })
 
     def test_serialize_call_events_request_list_to_json(self) -> None:
@@ -88,39 +89,39 @@ class TestMessageSerializer(unittest.TestCase):
                 "device_id": 1,
                 "event_number": 7,
                 "value": "value",
-                "platform": 1
-            }
+                "platform": 1,
+            },
         )
         request2 = CallEventsRequestFactory(
             event={
                 "device_id": 2,
                 "event_number": 8,
                 "value": "value2",
-                "platform": 2
-            }
+                "platform": 2,
+            },
         )
 
         result = MessageSerializer.serialize_list_to_json([request1, request2])
 
         self.assertEqual(json.loads(result), [
             {
-                'events': [
+                "events": [
                     {
-                        'deviceId': '1',
-                        'eventNumber': 7,
-                        'value': 'value',
-                        'platform': 1
-                    }
-                ]
+                        "deviceId": "1",
+                        "eventNumber": 7,
+                        "value": "value",
+                        "platform": 1,
+                    },
+                ],
             },
             {
-                'events': [
+                "events": [
                     {
-                        'deviceId': '2',
-                        'eventNumber': 8,
-                        'value': 'value2',
-                        'platform': 2
-                    }
-                ]
-            }
+                        "deviceId": "2",
+                        "eventNumber": 8,
+                        "value": "value2",
+                        "platform": 2,
+                    },
+                ],
+            },
         ])
diff --git a/tests/messages/test_validator.py b/tests/messages/test_validator.py
index 2f6e470..dab6984 100644
--- a/tests/messages/test_validator.py
+++ b/tests/messages/test_validator.py
@@ -1,6 +1,7 @@
 import unittest
-from mobilus_client.messages.validator import MessageValidator
+
 from mobilus_client.messages.status import MessageStatus
+from mobilus_client.messages.validator import MessageValidator
 from tests.factories import (
     CallEventsRequestFactory,
     CurrentStateRequestFactory,
diff --git a/tests/registries/test_key.py b/tests/registries/test_key.py
index 9b9ee30..4d5288d 100644
--- a/tests/registries/test_key.py
+++ b/tests/registries/test_key.py
@@ -1,16 +1,17 @@
 import unittest
-from mobilus_client.registries.key import KeyRegistry
-from tests.factories import (
-    LoginResponseFactory,
-)
+
 from mobilus_client.proto import (
     CallEventsRequest,
     CurrentStateRequest,
     CurrentStateResponse,
     DevicesListRequest,
     DevicesListResponse,
-    LoginResponse,
     LoginRequest,
+    LoginResponse,
+)
+from mobilus_client.registries.key import KeyRegistry
+from tests.factories import (
+    LoginResponseFactory,
 )
 
 
@@ -20,7 +21,7 @@ class TestKeyRegistry(unittest.TestCase):
         self.registry = KeyRegistry(self.user_key)
         self.login_response = LoginResponseFactory(
             private_key=b"test_private_key",
-            public_key=b"test_public_key"
+            public_key=b"test_public_key",
         )
         self.registry.register_keys(self.login_response)
 
diff --git a/tests/registries/test_message.py b/tests/registries/test_message.py
index d440eb0..dcf952f 100644
--- a/tests/registries/test_message.py
+++ b/tests/registries/test_message.py
@@ -1,4 +1,5 @@
 import unittest
+
 from mobilus_client.registries.message import MessageRegistry
 from tests.factories import (
     CallEventsRequestFactory,
diff --git a/tests/test_app.py b/tests/test_app.py
index 332ef34..36744b1 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -1,11 +1,12 @@
 import socket
 import unittest
 from unittest.mock import Mock, patch
+
 from mobilus_client.app import App
 from mobilus_client.config import Config
 from mobilus_client.mqtt_client import MqttClient
-from mobilus_client.registries.message import MessageRegistry
 from mobilus_client.registries.key import KeyRegistry
+from mobilus_client.registries.message import MessageRegistry
 from tests.factories import (
     CallEventsRequestFactory,
 )
@@ -18,7 +19,7 @@ class TestApp(unittest.TestCase):
             gateway_host="host",
             user_login="login",
             user_password="password",
-            timeout_period=0
+            timeout_period=0,
         )
         self.app = App(self.config)
 
@@ -29,13 +30,13 @@ class TestApp(unittest.TestCase):
         self.assertIsInstance(self.app.client, MqttClient)
 
     @patch.object(MqttClient, "connect", side_effect=socket.gaierror, autospec=True)
-    def test_call_with_invalid_gateway_host(self, mock_connect: Mock) -> None:
+    def test_call_with_invalid_gateway_host(self, _mock_connect: Mock) -> None:
         result = self.app.call([("call_events", {})])
 
         self.assertEqual(result, "[]")
 
     @patch.object(MqttClient, "connect", side_effect=TimeoutError, autospec=True)
-    def test_call_with_timeout_gateway_host(self, mock_connect: Mock) -> None:
+    def test_call_with_timeout_gateway_host(self, _mock_connect: Mock) -> None:
         result = self.app.call([("call_events", {})])
 
         self.assertEqual(result, "[]")
@@ -49,7 +50,7 @@ class TestApp(unittest.TestCase):
 
     @patch.object(MqttClient, "connect", return_value=Mock(), autospec=True)
     @patch.object(MqttClient, "loop_start", return_value=Mock(), autospec=True)
-    def test_call_with_not_authenticated(self, mock_loop_start: Mock, mock_connect: Mock) -> None:
+    def test_call_with_not_authenticated(self, _mock_loop_start: Mock, _mock_connect: Mock) -> None:
         self.config.auth_timeout_period = 0.0005
         result = self.app.call([("call_events", {})])
 
@@ -77,7 +78,7 @@ class TestApp(unittest.TestCase):
         self.app.client.authenticated_event.set()
         self.app.client.completed_event.set()
         call_events_request = CallEventsRequestFactory(
-            event={"device_id": 1, "event_number": 1, "value": "value", "platform": 1}
+            event={"device_id": 1, "event_number": 1, "value": "value", "platform": 1},
         )
         self.app.message_registry.register_response(call_events_request)
 
diff --git a/tests/test_config.py b/tests/test_config.py
index 14f4d79..3d6f7a8 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,4 +1,5 @@
 import unittest
+
 from mobilus_client.config import Config
 from mobilus_client.utils.encryption import create_key
 
diff --git a/tests/test_mqtt_client.py b/tests/test_mqtt_client.py
index 677089b..66d06d8 100644
--- a/tests/test_mqtt_client.py
+++ b/tests/test_mqtt_client.py
@@ -1,15 +1,16 @@
 import threading
 import unittest
 from unittest.mock import ANY, Mock, patch
+
 from mobilus_client.config import Config
-from mobilus_client.registries.key import KeyRegistry
-from mobilus_client.registries.message import MessageRegistry
 from mobilus_client.mqtt_client import MqttClient
 from mobilus_client.proto import (
     CallEventsRequest,
     CurrentStateRequest,
     DevicesListRequest,
 )
+from mobilus_client.registries.key import KeyRegistry
+from mobilus_client.registries.message import MessageRegistry
 from tests.factories import (
     CallEventsRequestFactory,
     CurrentStateResponseFactory,
@@ -26,7 +27,7 @@ class TestMQTTClient(unittest.TestCase):
             gateway_host="host",
             user_login="login",
             user_password="password",
-            timeout_period=0
+            timeout_period=0,
         )
 
         self.message_registry = MessageRegistry()
@@ -38,7 +39,7 @@ class TestMQTTClient(unittest.TestCase):
                 "config": self.config,
                 "key_registry": self.key_registry,
                 "message_registry": self.message_registry,
-            }
+            },
         )
 
     def test_init(self) -> None:
@@ -69,39 +70,41 @@ class TestMQTTClient(unittest.TestCase):
     @patch.object(MqttClient, "is_connected", return_value=True, autospec=True)
     @patch.object(MqttClient, "publish", return_value=Mock(), autospec=True)
     def test_send_request_with_login_request(
-            self, mock_publish: Mock, mock_is_connected: Mock, _mock_time: Mock) -> None:
+            self, mock_publish: Mock, _mock_is_connected: Mock, _mock_time: Mock) -> None:
         self.client.send_request("login", login="user", password=self.config.user_key)
 
         self.assertEqual(self.message_registry.get_requests(), [])
         mock_publish.assert_called_once_with(
             self.client,
             "module",
-            b"\x00\x00\x00\r\x01aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00\n\x04user\x12 " +
-            b"^\x88H\x98\xda(\x04qQ\xd0\xe5o\x8d\xc6)'s`=\rj\xab\xbd\xd6*\x11\xefr\x1d\x15B\xd8"
+            (
+                b"\x00\x00\x00\r\x01aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00\n\x04user\x12 "
+                b"^\x88H\x98\xda(\x04qQ\xd0\xe5o\x8d\xc6)'s`=\rj\xab\xbd\xd6*\x11\xefr\x1d\x15B\xd8"
+            ),
         )
 
     @patch("time.time", return_value=1633036800)
     @patch.object(MqttClient, "is_connected", return_value=True, autospec=True)
     @patch.object(MqttClient, "publish", return_value=Mock(), autospec=True)
     def test_send_request_with_call_events_request(
-            self, mock_publish: Mock, mock_is_connected: Mock, _mock_time: Mock) -> None:
+            self, mock_publish: Mock, _mock_is_connected: Mock, _mock_time: Mock) -> None:
         login_response = LoginResponseFactory(private_key=b"test_private_key")
         self.key_registry.register_keys(login_response)
 
-        self.client.send_request("call_events", device_id=0, value="value")
+        self.client.send_request("call_events", device_id="0", value="value")
 
         self.assertIsInstance(self.message_registry.get_requests()[0], CallEventsRequest)
         mock_publish.assert_called_once_with(
             self.client,
             "module",
-            b'\x00\x00\x00\r\raV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x003\xc2\x06\xb5\x9f&\x1b\xfcj2\xd2_\xd9'
+            b"\x00\x00\x00\r\raV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x003\xc2\x06\xb5\x9f&\x1b\xfcj2\xd2_\xd9",
         )
 
     @patch("time.time", return_value=1633036800)
     @patch.object(MqttClient, "is_connected", return_value=True, autospec=True)
     @patch.object(MqttClient, "publish", return_value=Mock(), autospec=True)
     def test_send_request_with_current_state_request(
-            self, mock_publish: Mock, mock_is_connected: Mock, _mock_time: Mock) -> None:
+            self, mock_publish: Mock, _mock_is_connected: Mock, _mock_time: Mock) -> None:
         login_response = LoginResponseFactory(private_key=b"test_private_key")
         self.key_registry.register_keys(login_response)
 
@@ -111,14 +114,14 @@ class TestMQTTClient(unittest.TestCase):
         mock_publish.assert_called_once_with(
             self.client,
             "module",
-            b'\x00\x00\x00\r\x1aaV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00'
+            b"\x00\x00\x00\r\x1aaV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00",
         )
 
     @patch("time.time", return_value=1633036800)
     @patch.object(MqttClient, "is_connected", return_value=True, autospec=True)
     @patch.object(MqttClient, "publish", return_value=Mock(), autospec=True)
     def test_send_request_with_devices_list_request(
-            self, mock_publish: Mock, mock_is_connected: Mock, _mock_time: Mock) -> None:
+            self, mock_publish: Mock, _mock_is_connected: Mock, _mock_time: Mock) -> None:
         login_response = LoginResponseFactory(private_key=b"test_private_key")
         self.key_registry.register_keys(login_response)
 
@@ -128,14 +131,14 @@ class TestMQTTClient(unittest.TestCase):
         mock_publish.assert_called_once_with(
             self.client,
             "module",
-            b'\x00\x00\x00\r\x03aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00'
+            b"\x00\x00\x00\r\x03aV*\x00\x01#Eg\x89\xab\xcd\xef\x04\x00",
         )
 
     @patch("logging.Logger.info", return_value=Mock(), autospec=True)
     def test_on_disconnect(self, mock_info: Mock) -> None:
-        self.client.on_disconnect(Mock(), None, 0)
+        self.client.on_disconnect(Mock(), {}, 0)
 
-        mock_info.assert_called_once_with(ANY, "Disconnected with result code - 0")
+        mock_info.assert_called_once_with(ANY, "Disconnected with result code - %s", 0)
 
     @patch.object(MqttClient, "subscribe", return_value=Mock(), autospec=True)
     def test_on_connect(self, mock_subscribe: Mock) -> None:
@@ -145,8 +148,8 @@ class TestMQTTClient(unittest.TestCase):
             self.client,
             [
                 (self.config.client_id, 0),
-                ("clients", 0)
-            ]
+                ("clients", 0),
+            ],
         )
 
     @patch.object(MqttClient, "send_request", return_value=Mock(), autospec=True)
@@ -175,7 +178,7 @@ class TestMQTTClient(unittest.TestCase):
         self.assertEqual(self.key_registry.get_keys(), {
             "user_key": self.config.user_key,
             "private_key": login_response.private_key,
-            "public_key": login_response.public_key
+            "public_key": login_response.public_key,
         })
 
     def test_on_message_call_events_request_all_completed(self) -> None:
@@ -191,7 +194,7 @@ class TestMQTTClient(unittest.TestCase):
             self.client, {
                 "config": self.config,
                 "key_registry": self.key_registry,
-                "message_registry": self.message_registry
+                "message_registry": self.message_registry,
             }, message)
         self.assertEqual(self.message_registry.get_responses(), [call_events_request])
         self.assertTrue(self.client.completed_event.is_set())
@@ -208,7 +211,7 @@ class TestMQTTClient(unittest.TestCase):
             self.client, {
                 "config": self.config,
                 "key_registry": self.key_registry,
-                "message_registry": self.message_registry
+                "message_registry": self.message_registry,
             }, message)
         self.assertEqual(self.message_registry.get_responses(), [current_state_response])
         self.assertFalse(self.client.completed_event.is_set())
@@ -225,7 +228,7 @@ class TestMQTTClient(unittest.TestCase):
             self.client, {
                 "config": self.config,
                 "key_registry": self.key_registry,
-                "message_registry": self.message_registry
+                "message_registry": self.message_registry,
             }, message)
         self.assertEqual(self.message_registry.get_responses(), [devices_list_response])
         self.assertFalse(self.client.completed_event.is_set())
diff --git a/tests/utils/test_encryption.py b/tests/utils/test_encryption.py
index d814fc2..c11edc2 100644
--- a/tests/utils/test_encryption.py
+++ b/tests/utils/test_encryption.py
@@ -1,20 +1,26 @@
 import unittest
 from hashlib import sha256
 from struct import pack
-from mobilus_client.utils.encryption import create_iv, create_key, decrypt_body, encrypt_body
+
+from mobilus_client.utils.encryption import (
+    create_iv,
+    create_key,
+    decrypt_body,
+    encrypt_body,
+)
 
 
 class TestEncryption(unittest.TestCase):
     def test_create_iv(self) -> None:
         value = 123456
-        expected_iv = bytearray(b'\x00' * 12) + pack('>I', value)
+        expected_iv = bytearray(b"\x00" * 12) + pack(">I", value)
         result = create_iv(value)
 
         self.assertEqual(result, expected_iv)
 
     def test_create_key(self) -> None:
         value = "test_key"
-        expected_key = sha256(value.encode('utf-8')).digest()
+        expected_key = sha256(value.encode("utf-8")).digest()
         result = create_key(value)
 
         self.assertEqual(result, expected_key)