Skip to content

Commit 5e74a80

Browse files
authored
Merge pull request #1834 from jacmet/feature/mqtt-optional-username-password
resource/mqtt: add optional username/password for MQTT authentication
2 parents 1064f73 + 022f552 commit 5e74a80

3 files changed

Lines changed: 24 additions & 5 deletions

File tree

doc/configuration.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ Arguments:
416416
"OFF"
417417
- avail_topic (str): topic that signals the availability of the Tasmota power
418418
outlet
419+
- username (str): Optional, MQTT username to authenticate as
420+
- password (str): Optional, MQTT password to authenticate with
419421

420422
Used by:
421423
- `TasmotaPowerDriver`_

labgrid/resource/mqtt.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,24 @@ class MQTTManager(ResourceManager):
1616
_topic_lock = attr.ib(default=threading.Lock())
1717
_last = attr.ib(default=0.0, validator=attr.validators.instance_of(float))
1818

19-
def _create_mqtt_connection(self, host):
19+
def _create_mqtt_connection(self, host, username, password):
2020
import paho.mqtt.client as mqtt
2121

2222
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
23+
client.username_pw_set(username, password)
2324
client.connect(host)
2425
client.on_message = self._on_message
2526
client.loop_start()
2627
return client
2728

2829
def on_resource_added(self, resource):
2930
host = resource.host
30-
if host not in self._clients:
31-
self._clients[host] = self._create_mqtt_connection(host)
32-
self._clients[host].subscribe(resource.avail_topic)
31+
username = resource.username
32+
password = resource.password
33+
key = f"{host}:{username or ''}:{password or ''}"
34+
if key not in self._clients:
35+
self._clients[key] = self._create_mqtt_connection(host, username, password)
36+
self._clients[key].subscribe(resource.avail_topic)
3337

3438
def _on_message(self, client, userdata, msg):
3539
payload = msg.payload.decode("utf-8")
@@ -57,6 +61,8 @@ class MQTTResource(ManagedResource):
5761

5862
host = attr.ib(validator=attr.validators.instance_of(str))
5963
avail_topic = attr.ib(validator=attr.validators.instance_of(str))
64+
username = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))
65+
password = attr.ib(default=None, validator=attr.validators.optional(attr.validators.instance_of(str)))
6066

6167
def __attrs_post_init__(self):
6268
self.timeout = 30.0

tests/test_tasmota.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,26 @@
99
def test_tasmota_resource(target, mocker):
1010
mocker.patch("paho.mqtt.client.Client.connect", return_value=None)
1111
mocker.patch("paho.mqtt.client.Client.loop_start", return_value=None)
12+
pw_set = mocker.patch("paho.mqtt.client.Client.username_pw_set", return_value=None)
1213
TasmotaPowerPort(target, name=None, host="localhost", avail_topic="test", power_topic="test", status_topic="test")
14+
pw_set.assert_called_once_with(None, None)
1315

1416

1517
def test_tasmota_driver(target, mocker):
1618
mocker.patch("paho.mqtt.client.Client.connect", return_value=None)
1719
mocker.patch("paho.mqtt.client.Client.loop_start", return_value=None)
20+
pw_set = mocker.patch("paho.mqtt.client.Client.username_pw_set", return_value=None)
1821
res = TasmotaPowerPort(
19-
target, name=None, host="localhost", avail_topic="test", power_topic="test", status_topic="test"
22+
target,
23+
name=None,
24+
host="localhost",
25+
avail_topic="test",
26+
power_topic="test",
27+
status_topic="test",
28+
username="foo",
29+
password="bar",
2030
)
2131
res.manager._available.add("test")
2232
driver = TasmotaPowerDriver(target, name=None)
2333
target.activate(driver)
34+
pw_set.assert_called_once_with("foo", "bar")

0 commit comments

Comments
 (0)