From 5bcf524c4dc844825b164b0f6905a504244a8f7e Mon Sep 17 00:00:00 2001 From: Ilyar Date: Mon, 25 May 2026 19:36:31 +0200 Subject: [PATCH] feat: expose MQTT proxy configuration --- README.md | 13 ++++++++++++ setup.py | 2 +- tb_device_mqtt.py | 4 ++++ tests/test_mqtt_proxy.py | 46 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/test_mqtt_proxy.py diff --git a/README.md b/README.md index cf3bc32..2646c58 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,19 @@ client.send_telemetry({'temperature': 41.9}, queued=False) ## Using Device APIs **TBDeviceMqttClient** provides access to Device MQTT APIs of ThingsBoard platform. It allows to publish telemetry and attribute updates, subscribe to attribute changes, send and receive RPC commands, etc. Use **TBHTTPClient** for the Device HTTP API. +#### Proxy configuration +You can configure an MQTT proxy before connecting the client. The arguments are forwarded to the underlying Paho MQTT client. + +```python +import socks +from tb_device_mqtt import TBDeviceMqttClient + + +client = TBDeviceMqttClient("127.0.0.1", username="A1_TEST_TOKEN") +client.proxy_set(proxy_type=socks.HTTP, proxy_addr="proxy.example.com", proxy_port=3128) +client.connect() +``` + #### Subscription to attributes You can subscribe to attribute updates from the server. The following example demonstrates how to subscribe to attribute updates from the server. ##### MQTT diff --git a/setup.py b/setup.py index 532b8a0..7423b7f 100644 --- a/setup.py +++ b/setup.py @@ -35,4 +35,4 @@ long_description_content_type="text/markdown", python_requires=">=3.9", packages=["."], - install_requires=['tb-paho-mqtt-client>=2.1.2', 'requests>=2.31.0', 'orjson']) + install_requires=['tb-paho-mqtt-client>=2.1.2', 'requests>=2.31.0', 'orjson', 'PySocks']) diff --git a/tb_device_mqtt.py b/tb_device_mqtt.py index 50c4f50..61429b4 100644 --- a/tb_device_mqtt.py +++ b/tb_device_mqtt.py @@ -661,6 +661,10 @@ def __request_firmware_info(self): def is_connected(self): return self.__is_connected + def proxy_set(self, **kwargs): + """Configure proxy settings for the underlying MQTT client.""" + return self._client.proxy_set(**kwargs) + def connect(self, callback=None, min_reconnect_delay=1, timeout=120, tls=False, ca_certs=None, cert_file=None, key_file=None, keepalive=120): """Connect to ThingsBoard. The callback will be called when the connection is established.""" diff --git a/tests/test_mqtt_proxy.py b/tests/test_mqtt_proxy.py new file mode 100644 index 0000000..80c363e --- /dev/null +++ b/tests/test_mqtt_proxy.py @@ -0,0 +1,46 @@ +# Copyright 2026. ThingsBoard +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from unittest.mock import Mock, patch + +from tb_device_mqtt import TBDeviceMqttClient +from tb_gateway_mqtt import TBGatewayMqttClient + + +class MqttProxyTest(unittest.TestCase): + + @patch('tb_device_mqtt.Thread.start') + def test_device_client_proxy_set_delegates_to_paho_client(self, _): + client = TBDeviceMqttClient('localhost', username='token') + client._client.proxy_set = Mock(return_value=None) + + client.proxy_set(proxy_type='HTTP', proxy_addr='proxy.local', proxy_port=3128) + + client._client.proxy_set.assert_called_once_with( + proxy_type='HTTP', proxy_addr='proxy.local', proxy_port=3128) + + @patch('tb_device_mqtt.Thread.start') + def test_gateway_client_inherits_proxy_set(self, _): + client = TBGatewayMqttClient('localhost', username='token') + client._client.proxy_set = Mock(return_value=None) + + client.proxy_set(proxy_type='HTTP', proxy_addr='proxy.local', proxy_port=3128) + + client._client.proxy_set.assert_called_once_with( + proxy_type='HTTP', proxy_addr='proxy.local', proxy_port=3128) + + +if __name__ == '__main__': + unittest.main()