Skip to content

Commit 97986a2

Browse files
author
J.A.R.V.I.S.
committed
v4.4.0: MQTT settings card + fallback + test connection button (Mac)
- Add complete MQTT settings card with enable checkbox, broker/port/user/pass/fallback fields - Add 'Verbindung testen' and 'Speichern' buttons - OnMqttAutoConfigure passes fallbackHost from UI - OnMqttTestConnection calls TestConnectionAsync - Save persists MqttBrokerFallback - Program.cs passes config.MqttBrokerFallback to AutoConfigureAsync - Fix RowDefinitions type (string -> RowDefinitions.Parse)
1 parent 627a655 commit 97986a2

2 files changed

Lines changed: 252 additions & 1 deletion

File tree

src/HaDeskLink/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ private static void RunMqttSetup(Config config)
115115

116116
try
117117
{
118-
var result = Task.Run(() => MqttSetupHelper.AutoConfigureAsync(config.HaUrl, config.HaToken)).GetAwaiter().GetResult();
118+
var fallbackHost = string.IsNullOrEmpty(config.MqttBrokerFallback) ? null : config.MqttBrokerFallback;
119+
var result = Task.Run(() => MqttSetupHelper.AutoConfigureAsync(config.HaUrl, config.HaToken, fallbackHost)).GetAwaiter().GetResult();
119120

120121
if (result.Success)
121122
{

src/HaDeskLink/Views/MainWindow.axaml.cs

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public partial class MainWindow : Window
3030
private TextBlock? _statusText;
3131
private TextBlock? _connectionLabel;
3232
private Button? _retryButton;
33+
private TextBox? _mqttFallbackBox;
34+
private Button? _btnMqttTest;
35+
private TextBlock? _mqttStatusLabel;
3336

3437
// ── Color Palette ──────────────────────────────────────────
3538
private static readonly IBrush BgBrush = new SolidColorBrush(Color.FromArgb(255, 26, 26, 46));
@@ -152,6 +155,208 @@ public MainWindow(Config config, HaApiClient api)
152155
}
153156
});
154157

158+
// ── MQTT Settings Card ─────────────────────────────────
159+
var mqttStatusLabel = new TextBlock
160+
{
161+
Text = GetMqttStatusText(config),
162+
FontSize = 12,
163+
Foreground = GrayBrush,
164+
TextWrapping = TextWrapping.Wrap,
165+
Margin = new Thickness(0, 4, 0, 0)
166+
};
167+
_mqttStatusLabel = mqttStatusLabel;
168+
169+
var mqttEnableCheck = new CheckBox
170+
{
171+
Content = "MQTT aktivieren",
172+
IsChecked = config.MqttEnabled,
173+
Foreground = Brushes.White,
174+
FontSize = 13
175+
};
176+
177+
var mqttBrokerBox = new TextBox
178+
{
179+
Text = config.MqttBroker,
180+
Watermark = "homeassistant.local",
181+
FontSize = 12,
182+
Margin = new Thickness(0, 2)
183+
};
184+
var mqttPortBox = new TextBox
185+
{
186+
Text = config.MqttPort.ToString(),
187+
Watermark = "1883",
188+
FontSize = 12,
189+
Margin = new Thickness(0, 2)
190+
};
191+
var mqttUserBox = new TextBox
192+
{
193+
Text = config.MqttUsername,
194+
Watermark = "Benutzername",
195+
FontSize = 12,
196+
Margin = new Thickness(0, 2)
197+
};
198+
var mqttPassBox = new TextBox
199+
{
200+
Text = config.MqttPassword,
201+
Watermark = "Passwort",
202+
FontSize = 12,
203+
PasswordChar = '•',
204+
Margin = new Thickness(0, 2)
205+
};
206+
var mqttFallbackBox = new TextBox
207+
{
208+
Text = config.MqttBrokerFallback,
209+
Watermark = "z.B. 192.168.1.100",
210+
FontSize = 12,
211+
Margin = new Thickness(0, 2),
212+
[ToolTip.TipProperty] = "Alternative MQTT-Broker-Adresse (z.B. lokale IP), falls die Hauptadresse nicht erreichbar ist"
213+
};
214+
_mqttFallbackBox = mqttFallbackBox;
215+
216+
var mqttSslCheck = new CheckBox
217+
{
218+
Content = "SSL/TLS verwenden",
219+
IsChecked = config.MqttUseSsl,
220+
Foreground = Brushes.White,
221+
FontSize = 13
222+
};
223+
224+
var mqttAutoBtn = new Button
225+
{
226+
Content = "🔧 Auto-Konfiguration",
227+
[ToolTip.TipProperty] = "MQTT-Broker automatisch über HA REST API konfigurieren",
228+
FontSize = 12,
229+
Background = AccentBrush,
230+
Foreground = Brushes.White,
231+
CornerRadius = new CornerRadius(6),
232+
Padding = new Thickness(12, 6)
233+
};
234+
235+
var mqttTestBtn = new Button
236+
{
237+
Content = "🔌 Verbindung testen",
238+
[ToolTip.TipProperty] = "MQTT-Verbindung mit den aktuellen Einstellungen testen",
239+
FontSize = 12,
240+
Background = AccentBrush,
241+
Foreground = Brushes.White,
242+
CornerRadius = new CornerRadius(6),
243+
Padding = new Thickness(12, 6)
244+
};
245+
_btnMqttTest = mqttTestBtn;
246+
247+
var mqttSaveBtn = new Button
248+
{
249+
Content = "💾 Speichern",
250+
[ToolTip.TipProperty] = "MQTT-Einstellungen speichern",
251+
FontSize = 12,
252+
Background = AccentBrush,
253+
Foreground = Brushes.White,
254+
CornerRadius = new CornerRadius(6),
255+
Padding = new Thickness(12, 6)
256+
};
257+
258+
// ── Wire MQTT events ────────────────────────────────────
259+
mqttAutoBtn.Click += async (s, e) =>
260+
{
261+
mqttAutoBtn.IsEnabled = false;
262+
mqttStatusLabel.Text = "⏳ Verbinde...";
263+
try
264+
{
265+
var fallbackHost = _mqttFallbackBox?.Text?.Trim();
266+
fallbackHost = string.IsNullOrEmpty(fallbackHost) ? null : fallbackHost;
267+
var result = await MqttSetupHelper.AutoConfigureAsync(config.HaUrl, config.HaToken, fallbackHost);
268+
if (result.Success)
269+
{
270+
mqttEnableCheck.IsChecked = true;
271+
mqttBrokerBox.Text = result.BrokerHost ?? "";
272+
mqttPortBox.Text = result.BrokerPort.ToString();
273+
mqttUserBox.Text = result.Username ?? "";
274+
mqttPassBox.Text = result.Password ?? "";
275+
mqttSslCheck.IsChecked = result.UseSsl;
276+
config.MqttEnabled = true;
277+
config.MqttBroker = result.BrokerHost ?? "";
278+
config.MqttPort = result.BrokerPort;
279+
config.MqttUsername = result.Username ?? "";
280+
config.MqttPassword = result.Password ?? "";
281+
config.MqttUseSsl = result.UseSsl;
282+
config.MqttBrokerFallback = _mqttFallbackBox?.Text?.Trim() ?? "";
283+
config.MqttAutoConfigured = true;
284+
config.Save();
285+
mqttStatusLabel.Text = $"✓ MQTT konfiguriert ({result.BrokerHost}:{result.BrokerPort})";
286+
}
287+
else if (result.MosquittoNotInstalled)
288+
mqttStatusLabel.Text = "⚠️ Mosquitto nicht gefunden. Bitte in HA installieren.";
289+
else
290+
mqttStatusLabel.Text = $"⚠️ Fehler: {result.ErrorMessage ?? "Unbekannt"}";
291+
}
292+
catch (Exception ex) { mqttStatusLabel.Text = $"✗ Fehler: {ex.Message}"; }
293+
finally { mqttAutoBtn.IsEnabled = true; }
294+
};
295+
296+
mqttTestBtn.Click += async (s, e) =>
297+
{
298+
mqttTestBtn.IsEnabled = false;
299+
mqttStatusLabel.Text = "⏳ Teste MQTT-Verbindung...";
300+
try
301+
{
302+
var broker = mqttBrokerBox.Text?.Trim() ?? "";
303+
if (string.IsNullOrEmpty(broker))
304+
{
305+
mqttStatusLabel.Text = "⚠️ Bitte Broker-Adresse eingeben";
306+
mqttTestBtn.IsEnabled = true;
307+
return;
308+
}
309+
if (!int.TryParse(mqttPortBox.Text?.Trim(), out var port) || port <= 0)
310+
port = 1883;
311+
var user = string.IsNullOrEmpty(mqttUserBox.Text?.Trim()) ? null : mqttUserBox.Text.Trim();
312+
var pass = string.IsNullOrEmpty(mqttPassBox.Text) ? null : mqttPassBox.Text;
313+
var ssl = mqttSslCheck.IsChecked ?? false;
314+
var ok = await MqttSetupHelper.TestConnectionAsync(broker, port, user, pass, ssl);
315+
if (ok)
316+
mqttStatusLabel.Text = $"✓ MQTT-Verbindung erfolgreich ({broker}:{port})";
317+
else
318+
mqttStatusLabel.Text = $"✗ Verbindung zu {broker}:{port} fehlgeschlagen";
319+
}
320+
catch (Exception ex) { mqttStatusLabel.Text = $"✗ Fehler: {ex.Message}"; }
321+
finally { mqttTestBtn.IsEnabled = true; }
322+
};
323+
324+
mqttSaveBtn.Click += (s, e) =>
325+
{
326+
config.MqttEnabled = mqttEnableCheck.IsChecked ?? false;
327+
config.MqttBroker = mqttBrokerBox.Text?.Trim() ?? "";
328+
if (int.TryParse(mqttPortBox.Text?.Trim(), out var p))
329+
config.MqttPort = p;
330+
config.MqttUsername = mqttUserBox.Text?.Trim() ?? "";
331+
config.MqttPassword = mqttPassBox.Text ?? "";
332+
config.MqttUseSsl = mqttSslCheck.IsChecked ?? false;
333+
config.MqttBrokerFallback = _mqttFallbackBox?.Text?.Trim() ?? "";
334+
config.MqttAutoConfigured = false;
335+
config.Save();
336+
mqttStatusLabel.Text = "✓ MQTT-Einstellungen gespeichert";
337+
};
338+
339+
root.Children.Add(new Border
340+
{
341+
Background = PanelBrush,
342+
CornerRadius = new CornerRadius(8),
343+
Padding = new Thickness(16, 12),
344+
Margin = new Thickness(16, 12, 16, 0),
345+
Child = new StackPanel
346+
{
347+
Spacing = 6,
348+
Children =
349+
{
350+
new TextBlock { Text = "📡 MQTT-Einstellungen", FontWeight = FontWeight.SemiBold, FontSize = 14, Foreground = Brushes.White },
351+
mqttEnableCheck,
352+
BuildMqttGrid(mqttBrokerBox, mqttPortBox, mqttUserBox, mqttPassBox, mqttFallbackBox),
353+
mqttSslCheck,
354+
new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, Children = { mqttAutoBtn, mqttTestBtn, mqttSaveBtn } },
355+
mqttStatusLabel
356+
}
357+
}
358+
});
359+
155360
// ── Footer ──────────────────────────────────────────────
156361
root.Children.Add(new StackPanel
157362
{
@@ -401,6 +606,51 @@ private static List<QuickActionItem> LoadQuickActions(Config config)
401606
catch { }
402607
return result;
403608
}
609+
610+
private static string GetMqttStatusText(Config config)
611+
{
612+
if (!config.MqttEnabled)
613+
return "○ Deaktiviert";
614+
if (!string.IsNullOrEmpty(config.MqttBroker))
615+
return $"● Verbunden ({config.MqttBroker}:{config.MqttPort})";
616+
return "● Getrennt";
617+
}
618+
619+
private static Grid BuildMqttGrid(TextBox brokerBox, TextBox portBox, TextBox userBox, TextBox passBox, TextBox fallbackBox)
620+
{
621+
var grid = new Grid
622+
{
623+
ColumnDefinitions = ColumnDefinitions.Parse("120,*"),
624+
RowDefinitions = RowDefinitions.Parse("Auto,Auto,Auto,Auto,Auto")
625+
};
626+
627+
grid.Children.Add(new TextBlock { Text = "Broker:", [Grid.RowProperty] = 0, [Grid.ColumnProperty] = 0, VerticalAlignment = VerticalAlignment.Center, Foreground = GrayBrush, FontSize = 12 });
628+
grid.Children.Add(brokerBox);
629+
Grid.SetColumn(brokerBox, 1); Grid.SetRow(brokerBox, 0);
630+
brokerBox.Margin = new Thickness(4, 2);
631+
632+
grid.Children.Add(new TextBlock { Text = "Port:", [Grid.RowProperty] = 1, [Grid.ColumnProperty] = 0, VerticalAlignment = VerticalAlignment.Center, Foreground = GrayBrush, FontSize = 12 });
633+
grid.Children.Add(portBox);
634+
Grid.SetColumn(portBox, 1); Grid.SetRow(portBox, 1);
635+
portBox.Margin = new Thickness(4, 2);
636+
637+
grid.Children.Add(new TextBlock { Text = "Benutzername:", [Grid.RowProperty] = 2, [Grid.ColumnProperty] = 0, VerticalAlignment = VerticalAlignment.Center, Foreground = GrayBrush, FontSize = 12 });
638+
grid.Children.Add(userBox);
639+
Grid.SetColumn(userBox, 1); Grid.SetRow(userBox, 2);
640+
userBox.Margin = new Thickness(4, 2);
641+
642+
grid.Children.Add(new TextBlock { Text = "Passwort:", [Grid.RowProperty] = 3, [Grid.ColumnProperty] = 0, VerticalAlignment = VerticalAlignment.Center, Foreground = GrayBrush, FontSize = 12 });
643+
grid.Children.Add(passBox);
644+
Grid.SetColumn(passBox, 1); Grid.SetRow(passBox, 3);
645+
passBox.Margin = new Thickness(4, 2);
646+
647+
grid.Children.Add(new TextBlock { Text = "Fallback-Adresse:", [Grid.RowProperty] = 4, [Grid.ColumnProperty] = 0, VerticalAlignment = VerticalAlignment.Center, Foreground = GrayBrush, FontSize = 12 });
648+
grid.Children.Add(fallbackBox);
649+
Grid.SetColumn(fallbackBox, 1); Grid.SetRow(fallbackBox, 4);
650+
fallbackBox.Margin = new Thickness(4, 2);
651+
652+
return grid;
653+
}
404654
}
405655

406656
public class QuickActionItem

0 commit comments

Comments
 (0)