Skip to content

Commit

Permalink
Added option to provide an existing mqtt client.
Browse files Browse the repository at this point in the history
  • Loading branch information
vschroeter committed Oct 16, 2024
1 parent 7255aaa commit 11f3ed6
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Using MQTT discoverable devices lets us add new sensors and devices to HA withou
- [Text](#text)
- [Usage](#usage-8)
- [FAQ](#faq)
- [Using an existing MQTT client](#using-an-existing-mqtt-client)
- [I'm having problems on 32 bit ARM](#im-having-problems-on-32-bit-arm)
- [Contributing](#contributing)
- [Users of ha-mqtt-discoverable](#users-of-ha-mqtt-discoverable)
Expand Down Expand Up @@ -491,6 +492,26 @@ my_text.set_text("Some awesome text")

## FAQ

### Using an existing MQTT client

If you want to use an existing MQTT client for the connection, you can pass it to the `Settings` object:

```py
from ha_mqtt_discoverable import Settings
from paho.mqtt.client import Client

# Creating the MQTT client
client = Client()
# Doing other stuff with the client, like connecting to the broker
# ...

# Providing the client to the Settings object
# In this case, no other MQTT settings are needed
mqtt_settings = Settings.MQTT(client=client)

# Continue with the rest of the code as usual
```

### I'm having problems on 32 bit ARM

Pydantic 2 has issues on 32 bit ARM. More details are on [ha-mqtt-discoverable/pull/191](https://github.com/unixorn/ha-mqtt-discoverable/pull/191). TL;DR: If you're on an ARM32 machine you're going to have to pin to the 0.13.1 version.
Expand Down
28 changes: 22 additions & 6 deletions ha_mqtt_discoverable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import logging
import ssl
from importlib import metadata
from typing import Any, Callable, Generic, Optional, TypeVar
from typing import Any, Callable, Generic, Optional, TypeVar, Union

import paho.mqtt.client as mqtt
from paho.mqtt.client import MQTTMessageInfo
Expand Down Expand Up @@ -546,7 +546,11 @@ class Settings(BaseModel, Generic[EntityType]):
class MQTT(BaseModel):
"""Connection settings for the MQTT broker"""

host: str
class Config:
# To use mqtt.Client
arbitrary_types_allowed = True

host: Optional[str] = "homeassistant"
port: Optional[int] = 1883
username: Optional[str] = None
password: Optional[str] = None
Expand All @@ -561,6 +565,9 @@ class MQTT(BaseModel):
state_prefix: str = "hmd"
"""The root of the topic tree ha-mqtt-discovery publishes its state messages"""

client: Optional[mqtt.Client] = None
"""Optional MQTT client to use for the connection. If provided, most other settings are ignored."""

mqtt: MQTT
"""Connection to MQTT broker"""
entity: EntityType
Expand Down Expand Up @@ -640,7 +647,7 @@ def __init__(self, settings: Settings[EntityType], on_connect: Optional[Callable
self._setup_client(on_connect)
# If there is a callback function defined, the user must manually connect
# to the MQTT client
if not on_connect:
if not (on_connect or self._settings.mqtt.client is not None):
self._connect_client()

def __str__(self) -> str:
Expand All @@ -658,9 +665,16 @@ def __str__(self) -> str:

def _setup_client(self, on_connect: Optional[Callable] = None) -> None:
"""Create an MQTT client and setup some basic properties on it"""

# If the user has passed in an MQTT client, use it
if self._settings.mqtt.client:
self.mqtt_client = self._settings.mqtt.client
return

mqtt_settings = self._settings.mqtt
logger.debug(f"Creating mqtt client ({mqtt_settings.client_name}) for " f"{mqtt_settings.host}:{mqtt_settings.port}")
self.mqtt_client = mqtt.Client(mqtt_settings.client_name)
# Use named parameter to add compatibility with paho-mqtt >2.0.0
self.mqtt_client = mqtt.Client(client_id=mqtt_settings.client_name)
if mqtt_settings.tls_key:
logger.info(f"Connecting to {mqtt_settings.host}:{mqtt_settings.port} with SSL and client certificate authentication")
logger.debug(f"ca_certs={mqtt_settings.tls_ca_cert}")
Expand Down Expand Up @@ -703,7 +717,7 @@ def _setup_client(self, on_connect: Optional[Callable] = None) -> None:
def _connect_client(self) -> None:
"""Connect the client to the MQTT broker, start its onw internal loop in
a separate thread"""
result = self.mqtt_client.connect(self._settings.mqtt.host, self._settings.mqtt.port)
result = self.mqtt_client.connect(self._settings.mqtt.host, self._settings.mqtt.port or 1883)
# Check if we have established a connection
if result != mqtt.MQTT_ERR_SUCCESS:
raise RuntimeError("Error while connecting to MQTT broker")
Expand All @@ -712,7 +726,9 @@ def _connect_client(self) -> None:
# messages in a separate thread
self.mqtt_client.loop_start()

def _state_helper(self, state: Optional[str], topic: Optional[str] = None, retain=True) -> MQTTMessageInfo:
def _state_helper(
self, state: Optional[Union[str, float, int]], topic: Optional[str] = None, retain=True
) -> Optional[MQTTMessageInfo]:
"""
Write a state to the given MQTT topic, returning the result of client.publish()
"""
Expand Down

0 comments on commit 11f3ed6

Please sign in to comment.