@@ -39,29 +39,37 @@ public class MqttSetupResult
3939/// <summary>
4040/// Auto-configures MQTT by querying the Home Assistant REST API for
4141/// the MQTT broker details, then testing the connection.
42+ ///
43+ /// Strategy:
44+ /// 1. Check if MQTT integration exists in HA (via /api/config -> components)
45+ /// 2. Try to extract broker details from config entries API (credentials may be hidden)
46+ /// 3. Use the HA host as broker host (Mosquitto add-on runs on same host)
47+ /// 4. Try multiple connection strategies: discovered creds, anonymous, HA token
4248/// </summary>
4349public static class MqttSetupHelper
4450{
4551 /// <summary>
4652 /// Query the HA REST API for MQTT integration details and try to
47- /// establish a working MQTT connection. Returns a result that can
48- /// be used directly for persistent MQTT client setup .
53+ /// establish a working MQTT connection. Uses the configured HA URL
54+ /// (works with IP addresses AND domain names like home.kirchweger.de) .
4955 /// </summary>
5056 public static async Task < MqttSetupResult > AutoConfigureAsync ( string haUrl , string haToken )
5157 {
5258 var result = new MqttSetupResult ( ) ;
5359
5460 try
5561 {
56- // ── Step 1: extract the HA host from the URL ──
62+ // ── Step 1: extract the host from the configured HA URL ──
63+ // This works with IPs (192.168.178.33) AND domains (home.kirchweger.de)
5764 var haUri = new Uri ( haUrl . TrimEnd ( '/' ) ) ;
5865 var haHost = haUri . Host ;
66+ var haPort = haUri . Port ; // -1 if not specified, else the actual port
5967
60- // Build an HttpClient with the Bearer token pre-set.
6168 using var http = new HttpClient { Timeout = TimeSpan . FromSeconds ( 10 ) } ;
6269 http . DefaultRequestHeaders . Add ( "Authorization" , $ "Bearer { haToken } ") ;
6370
64- // ── Step 2: fetch config entries, look for the MQTT integration ──
71+ // ── Step 2: check if MQTT integration exists in HA ──
72+ bool mqttInstalled = false ;
6573 string ? brokerHost = null ;
6674 int brokerPort = 0 ;
6775 string ? brokerUser = null ;
@@ -70,20 +78,54 @@ public static async Task<MqttSetupResult> AutoConfigureAsync(string haUrl, strin
7078
7179 try
7280 {
73- var configUrl = $ " { haUrl } /api/config/config_entries/entry" ;
74- var resp = await http . GetStringAsync ( configUrl ) ;
75- using var doc = JsonDocument . Parse ( resp ) ;
81+ // Check /api/config -> components list for "mqtt"
82+ var configResp = await http . GetStringAsync ( $ " { haUrl . TrimEnd ( '/' ) } /api/config" ) ;
83+ using var configDoc = JsonDocument . Parse ( configResp ) ;
7684
77- if ( doc . RootElement . ValueKind == JsonValueKind . Array )
85+ if ( configDoc . RootElement . TryGetProperty ( "components" , out var components )
86+ && components . ValueKind == JsonValueKind . Array )
7887 {
79- foreach ( var entry in doc . RootElement . EnumerateArray ( ) )
88+ foreach ( var comp in components . EnumerateArray ( ) )
89+ {
90+ if ( comp . GetString ( ) == "mqtt" )
91+ {
92+ mqttInstalled = true ;
93+ break ;
94+ }
95+ }
96+ }
97+ }
98+ catch
99+ {
100+ // If we can't reach the API, assume MQTT might be there and try anyway
101+ mqttInstalled = true ; // give it a shot
102+ }
103+
104+ // ── Step 3: try to extract broker details from config entries ──
105+ // HA protects the actual credentials, so we may get empty data here.
106+ // But sometimes the broker host is visible.
107+ try
108+ {
109+ var entriesResp = await http . GetStringAsync ( $ "{ haUrl . TrimEnd ( '/' ) } /api/config/config_entries/entry") ;
110+ using var entriesDoc = JsonDocument . Parse ( entriesResp ) ;
111+
112+ if ( entriesDoc . RootElement . ValueKind == JsonValueKind . Array )
113+ {
114+ foreach ( var entry in entriesDoc . RootElement . EnumerateArray ( ) )
80115 {
81116 var domain = entry . TryGetProperty ( "domain" , out var d ) ? d . GetString ( ) : null ;
82117 if ( domain != "mqtt" ) continue ;
83118
84- // We found the MQTT integration – extract the connection details.
119+ // Found the MQTT config entry
120+ if ( entry . TryGetProperty ( "title" , out var title ) )
121+ {
122+ // "core-mosquitto" title means Mosquitto add-on
123+ // This confirms the broker is on the same host
124+ }
125+
85126 if ( entry . TryGetProperty ( "data" , out var data ) )
86127 {
128+ // Broker host might be available
87129 if ( data . TryGetProperty ( "broker" , out var b ) )
88130 brokerHost = b . GetString ( ) ;
89131 if ( data . TryGetProperty ( "port" , out var p ) && p . TryGetInt32 ( out var portVal ) )
@@ -101,31 +143,40 @@ public static async Task<MqttSetupResult> AutoConfigureAsync(string haUrl, strin
101143 }
102144 catch
103145 {
104- // Fallback: the /api/config/config_entries/entry endpoint might not
105- // exist on older HA versions — carry on with the heuristic below.
146+ // Config entries API may not return credentials, that's fine
106147 }
107148
108- // ── Step 3: heuristic fallback ──
109- // The MQTT broker (Mosquitto add-on) nearly always runs on the
110- // same host as Home Assistant itself.
149+ // ── Step 4: determine broker host ──
150+ // If the API didn't reveal the broker host, use the HA host.
151+ // For domain names (home.kirchweger.de), this means the MQTT broker
152+ // is accessible at the same domain. For IPs, same thing.
111153 if ( string . IsNullOrEmpty ( brokerHost ) )
112154 {
113155 brokerHost = haHost ;
114156 }
115157
116- // Default port unless we found one from the API.
158+ // Default port unless we found one from the API
117159 if ( brokerPort <= 0 )
118160 {
161+ // Check if HA is running on a custom port — Mosquitto is
162+ // always on 1883 (or 8883 for SSL) regardless of HA's port
119163 brokerPort = brokerSsl ? 8883 : 1883 ;
120164 }
121165
122166 result . BrokerHost = brokerHost ;
123167 result . BrokerPort = brokerPort ;
124168 result . UseSsl = brokerSsl ;
125169
126- // ── Step 4: attempt to connect ──
170+ // ── Step 5: check if MQTT is installed at all ──
171+ if ( ! mqttInstalled )
172+ {
173+ // Double-check by trying to connect anyway — the components
174+ // list might not include all loaded integrations
175+ }
176+
177+ // ── Step 6: attempt to connect with multiple strategies ──
127178
128- // Strategy A: with discovered credentials (or anonymous if none)
179+ // Strategy A: with discovered credentials (or anonymous if none found )
129180 if ( await TestConnectionAsync ( brokerHost , brokerPort , brokerUser , brokerPass , brokerSsl ) )
130181 {
131182 result . Success = true ;
@@ -134,36 +185,60 @@ public static async Task<MqttSetupResult> AutoConfigureAsync(string haUrl, strin
134185 return result ;
135186 }
136187
137- // Strategy B: try the HA access token as the MQTT password.
138- // Some HA setups (especially supervised/core installs) accept this.
139- if ( ! string . IsNullOrEmpty ( haToken ) && brokerUser != haToken )
188+ // Strategy B: try anonymous access (Mosquitto add-on default allows
189+ // anonymous from local network)
190+ if ( brokerUser != null || brokerPass != null )
140191 {
141- if ( await TestConnectionAsync ( brokerHost , brokerPort , brokerUser , haToken , brokerSsl ) )
192+ if ( await TestConnectionAsync ( brokerHost , brokerPort , null , null , brokerSsl ) )
193+ {
194+ result . Success = true ;
195+ result . Username = null ;
196+ result . Password = null ;
197+ return result ;
198+ }
199+ }
200+
201+ // Strategy C: try with the HA access token as MQTT password
202+ // Some HA setups accept this
203+ if ( ! string . IsNullOrEmpty ( haToken ) )
204+ {
205+ if ( await TestConnectionAsync ( brokerHost , brokerPort , "homeassistant" , haToken , brokerSsl ) )
142206 {
143207 result . Success = true ;
144- result . Username = brokerUser ;
208+ result . Username = "homeassistant" ;
145209 result . Password = haToken ;
146210 return result ;
147211 }
148212 }
149213
150- // Strategy C: anonymous fallback (in case credentials are wrong, but
151- // the Mosquitto add-on allows anonymous connections from the local LAN).
152- if ( ! string . IsNullOrEmpty ( brokerUser ) || ! string . IsNullOrEmpty ( brokerPass ) )
214+ // Strategy D: if HA runs on a non-standard port, try localhost
215+ // ( Mosquitto might only listen on localhost in some setups)
216+ if ( brokerHost != "localhost" && brokerHost != "127.0.0.1" )
153217 {
154- if ( await TestConnectionAsync ( brokerHost , brokerPort , null , null , brokerSsl ) )
218+ if ( await TestConnectionAsync ( "localhost" , brokerPort , null , null , brokerSsl ) )
155219 {
156220 result . Success = true ;
221+ result . BrokerHost = "localhost" ;
222+ result . Username = null ;
223+ result . Password = null ;
224+ return result ;
225+ }
226+
227+ if ( await TestConnectionAsync ( "127.0.0.1" , brokerPort , null , null , brokerSsl ) )
228+ {
229+ result . Success = true ;
230+ result . BrokerHost = "127.0.0.1" ;
157231 result . Username = null ;
158232 result . Password = null ;
159233 return result ;
160234 }
161235 }
162236
163- // ── Step 5 : all attempts failed ──
237+ // ── Step 7 : all attempts failed ──
164238 result . MosquittoNotInstalled = true ;
165239 result . ErrorMessage = "Could not connect to the MQTT broker. " +
166- "Please make sure the Mosquitto broker add-on is installed and running in Home Assistant." ;
240+ "Please make sure the Mosquitto broker add-on is installed and running in Home Assistant " +
241+ "(Settings → Add-ons → Mosquitto Broker)." ;
167242 }
168243 catch ( Exception ex )
169244 {
@@ -219,4 +294,4 @@ public static async Task<bool> TestConnectionAsync(
219294 return false ;
220295 }
221296 }
222- }
297+ }
0 commit comments