Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore messages on shared topic when not authenticated #30

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Ignore messages on shared topic when not authenticated
zpieslak committed Nov 17, 2024

Verified

This commit was signed with the committer’s verified signature.
fwyzard Andrea Bocci
commit 148da96a346125da029cf9464fb306c672937501
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,9 @@

![test workflow](https://github.com/zpieslak/mobilus-client/actions/workflows/test.yml/badge.svg)

This code provides a native Python client for the Mobilus Cosmo GTW. It connects directly to the gateway's MQTT broker and sends message commands to control the associated devices. The connection is established locally, so the client must be run on the same network as the gateway. Currently, the only tested and supported devices are radio shutters (Mobilus COSMO 2WAY).
This code provides a native Python client for the Mobilus Cosmo GTW. It connects directly to the gateway's MQTT broker and sends message commands to control the associated devices. The connection is established locally, so the client must be run on the same network as the gateway. Note: internet access is not required and can be disabled on the device.

Currently, the only tested and supported devices are radio shutters (Mobilus COSMO 2WAY).

In order to use the client, configuration and setup need to be done on the Mobilus Cosmo GTW side. This includes creating a user and pairing the devices with the gateway.

1 change: 0 additions & 1 deletion mobilus_client/app.py
Original file line number Diff line number Diff line change
@@ -35,7 +35,6 @@ def call(self, commands: list[tuple[str, dict[str, str]]]) -> str:
if not client.connect_and_authenticate():
return self._empty_response()


# Execute the provided commands
for command, params in commands:
client.send_request(command, **params)
11 changes: 9 additions & 2 deletions mobilus_client/client.py
Original file line number Diff line number Diff line change
@@ -25,6 +25,8 @@ def __init__(
self, client_id: str, config: Config, key_registry: KeyRegistry, message_registry: MessageRegistry) -> None:
self.config = config
self.client_id = client_id
self.shared_topic = "clients"
self.command_topic = "module"
self.key_registry = key_registry
self.message_registry = message_registry
self.authenticated_event = threading.Event()
@@ -68,7 +70,7 @@ def send_request(self, command: str, **params: str | bytes | int | None) -> None
self.key_registry,
)

self.mqtt_client.publish("module", encrypted_message)
self.mqtt_client.publish(self.command_topic, encrypted_message)

def terminate(self) -> None:
self.mqtt_client.disconnect()
@@ -81,7 +83,7 @@ def on_connect_callback(
self, _client: mqtt.Client, _userdata: None, _flags: dict[str, Any], _reason_code: int) -> None:
self.mqtt_client.subscribe([
(self.client_id, 0),
("clients", 0),
(self.shared_topic, 0),
])

def on_subscribe_callback(self, _client: mqtt.Client, _userdata: None, _mid: int, _granted_qos: tuple[int]) -> None:
@@ -94,6 +96,11 @@ def on_subscribe_callback(self, _client: mqtt.Client, _userdata: None, _mid: int
def on_message_callback(self, _client: mqtt.Client, _userdata: None, mqtt_message: mqtt.MQTTMessage) -> None:
logger.info("Received message on topic - %s", mqtt_message.topic)

# Ignore messages from the shared topic sent by other clients until the client is authenticated.
if mqtt_message.topic == self.shared_topic and not self.authenticated_event.is_set():
logger.info("Client is not authenticated yet. Ignoring message.")
return

message = MessageEncryptor.decrypt(mqtt_message.payload, self.key_registry)
logger.info("Decrypted message - %s", type(message).__name__)

8 changes: 8 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -245,3 +245,11 @@ def test_on_message_devices_list_response_not_all_completed(self) -> None:
self.client.on_message_callback(self.client.mqtt_client, None, message)
self.assertEqual(self.message_registry.get_responses(), [devices_list_response])
self.assertFalse(self.client.completed_event.is_set())

def test_on_message_shared_topic_when_not_authenticated(self) -> None:
login_response = LoginResponseFactory(private_key=b"test_private_key")
devices_list_response = DevicesListResponseFactory()
encrypted_message = encrypt_message(devices_list_response, login_response.private_key)
message = Mock(payload=encrypted_message, topic="clients")

self.client.on_message_callback(self.client.mqtt_client, None, message)