LAN-Scanner: Rückgängig-Zustand nach Entfernen der festen Lease erklären
Build / build (push) Canceled after 0s

Nutzer-Feedback: Nach "Feste IP zuweisen" → "Rückgängig machen" wirkte die Zeile wie
kaputt (Button einfach ausgegraut, Status nur "Kein DHCP-Lease"). Technischer Hintergrund
bestätigt: RouterOS kennt kein "wieder dynamisch machen", UndoStaticAsync entfernt den
Lease-Eintrag komplett — das Gerät merkt das aber nicht sofort, sondern hält seine IP
client-seitig, bis es sich neu meldet (Renewal-Timer oder Trennen/Neuverbinden). Die Zeile
zeigt das jetzt explizit ("Feste IP entfernt" + erklärender Hinweistext/Tooltip statt
kommentarlosem Ausgrauen). Zusätzlich: immer sichtbarer "Aktualisieren"-Button, um nach
einem Trennen/Neuverbinden ohne Schritt-Wechsel neu zu scannen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Sonnet 5 committed 2026-09-17 19:15:15 +02:00
1 parent 84f5ab21db
commit 2c2fae73eb
4 files changed
+45 -7

No files matched your search

+3 -1
View File
@@ -34,7 +34,9 @@ Bei einem bereits konfigurierten Bestandsgerät liest der dritte Schritt automat
### Geräte im Netzwerk (LAN-Scanner)
Der vierte Schritt führt DHCP-Leases (`/ip/dhcp-server/lease`), die ARP-Tabelle (`/ip/arp`) und die Bridge-Host-Tabelle (`/interface/bridge/host`) per MAC-Adresse zu einer Geräteliste zusammen, gruppiert nach dem physischen Anschluss, an dem ein Gerät tatsächlich hängt (aus der Bridge-Host-Tabelle — ARP/Lease zeigen bei gebrückten Setups sonst nur die Bridge selbst, nicht den echten Port). Rein informativ bis auf eine Aktion: Bei einem Gerät mit DHCP-Lease lässt sich diese per Klick fest zuweisen (RouterOS „Make Static") oder wieder entfernen, damit es beim nächsten DHCP-Kontakt wieder eine dynamische Adresse bekommt. Wie die IST-Zustand-Analyse verändert dieser Schritt nichts an der vom Assistenten geplanten Soll-Konfiguration.
Der vierte Schritt führt DHCP-Leases (`/ip/dhcp-server/lease`), die ARP-Tabelle (`/ip/arp`) und die Bridge-Host-Tabelle (`/interface/bridge/host`) per MAC-Adresse zu einer Geräteliste zusammen, gruppiert nach dem physischen Anschluss, an dem ein Gerät tatsächlich hängt (aus der Bridge-Host-Tabelle — ARP/Lease zeigen bei gebrückten Setups sonst nur die Bridge selbst, nicht den echten Port). Rein informativ bis auf eine Aktion: Bei einem Gerät mit DHCP-Lease lässt sich diese per Klick fest zuweisen (RouterOS „Make Static") oder wieder entfernen. Ein „Aktualisieren"-Button lädt die Liste jederzeit neu.
RouterOS kennt keinen direkten „wieder dynamisch machen"-Befehl — das Rückgängig-Machen entfernt den Lease-Eintrag komplett. Das betroffene Gerät merkt das aber nicht sofort: Es hält seine bisherige IP-Adresse client-seitig weiter, bis es von sich aus eine neue DHCP-Anfrage stellt (Lease-Renewal-Timer) oder der Link neu aufgebaut wird (Kabel/WLAN trennen und neu verbinden, Neustart). Bis dahin zeigt die Zeile „Feste IP entfernt" mit deaktiviertem Button und einem erklärenden Hinweistext, statt kommentarlos auszugrauen. Wie die IST-Zustand-Analyse verändert dieser Schritt nichts an der vom Assistenten geplanten Soll-Konfiguration.
### Experten-Modus
@@ -17,6 +17,7 @@ public partial class LanScanDeviceRowViewModel : ObservableObject
[NotifyPropertyChangedFor(nameof(StatusLabel))]
[NotifyPropertyChangedFor(nameof(ActionLabel))]
[NotifyPropertyChangedFor(nameof(CanToggleStatic))]
[NotifyPropertyChangedFor(nameof(ActionHint))]
private DiscoveredDevice _device;
[ObservableProperty]
@@ -26,6 +27,18 @@ public partial class LanScanDeviceRowViewModel : ObservableObject
[ObservableProperty]
private string? _errorMessage;
/// <summary>
/// Gesetzt, direkt nachdem für dieses Gerät eine feste Lease entfernt wurde. RouterOS kennt danach
/// keinen Lease-Eintrag mehr für dieses Gerät — das Gerät selbst merkt das aber nicht sofort: Es hält
/// seine bisherige IP-Adresse client-seitig weiter, bis es von sich aus eine neue DHCP-Anfrage stellt
/// (Lease-Renewal-Timer) oder der Link neu aufgebaut wird (Kabel/WLAN trennen und neu verbinden,
/// Neustart). Rein für die Anzeige — wird bei jedem Neuladen des Schritts wieder zurückgesetzt.
/// </summary>
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(StatusLabel))]
[NotifyPropertyChangedFor(nameof(ActionHint))]
private bool _isAwaitingNewLease;
public string MacAddress => Device.MacAddress;
public string IpAddressDisplay => Device.IpAddress ?? "Keine IP bekannt";
@@ -36,6 +49,7 @@ public partial class LanScanDeviceRowViewModel : ObservableObject
{
{ IsStaticLease: true } => "Feste IP-Adresse",
{ HasDhcpLease: true } => "Automatisch per DHCP",
_ when IsAwaitingNewLease => "Feste IP entfernt",
_ => "Kein DHCP-Lease",
};
@@ -43,4 +57,9 @@ public partial class LanScanDeviceRowViewModel : ObservableObject
/// <summary>Ohne DHCP-Lease-Eintrag gibt es nichts, das umgestellt werden könnte.</summary>
public bool CanToggleStatic => !IsBusy && Device.LeaseId is not null;
/// <summary>Erklärt, warum der Button gerade deaktiviert ist, nachdem eine feste Zuweisung entfernt wurde.</summary>
public string? ActionHint => IsAwaitingNewLease && Device.LeaseId is null
? "Erst wieder möglich, sobald sich das Gerät neu meldet — üblicherweise nach Trennen/Neuverbinden oder wenn seine bisherige Lease-Zeit abläuft, nicht sofort."
: null;
}
@@ -47,6 +47,7 @@ public partial class LanScanStepViewModel : WizardStepViewModelBase
public override Task OnActivatedAsync() => LoadAsync();
/// <summary>Auch als manuelles "Aktualisieren" genutzt — z. B. um nach einem Trennen/Neuverbinden zu prüfen, ob ein Gerät wieder eine DHCP-Lease bekommen hat.</summary>
[RelayCommand]
private Task Retry() => LoadAsync();
@@ -99,9 +100,15 @@ public partial class LanScanStepViewModel : WizardStepViewModelBase
try
{
row.Device = row.Device.IsStaticLease
? await LanScanService.UndoStaticAsync(_session.Client, row.Device)
: await LanScanService.MakeStaticAsync(_session.Client, row.Device);
if (row.Device.IsStaticLease)
{
row.Device = await LanScanService.UndoStaticAsync(_session.Client, row.Device);
row.IsAwaitingNewLease = true;
}
else
{
row.Device = await LanScanService.MakeStaticAsync(_session.Client, row.Device);
}
}
catch (RouterOsException ex)
{
@@ -8,7 +8,15 @@
mc:Ignorable="d"
d:DesignWidth="600">
<StackPanel Margin="0,16,0,0">
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Margin="0,0,0,20" Opacity="0.8" />
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Margin="0,0,0,12" Opacity="0.8" />
<ui:Button Content="Aktualisieren" Appearance="Secondary" HorizontalAlignment="Left" Margin="0,0,0,16"
IsEnabled="{Binding IsLoading, Converter={StaticResource InverseBooleanConverter}}"
Command="{Binding RetryCommand}">
<ui:Button.Icon>
<ui:SymbolIcon Symbol="ArrowSync24" />
</ui:Button.Icon>
</ui:Button>
<StackPanel Orientation="Horizontal" Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}">
<ui:ProgressRing IsIndeterminate="True" Width="20" Height="20" Margin="0,0,12,0" />
@@ -17,8 +25,6 @@
<StackPanel Visibility="{Binding ErrorMessage, Converter={StaticResource StringToVisibilityConverter}}">
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" Foreground="{StaticResource MikrotikRedBrush}" Margin="0,0,0,12" />
<ui:Button Content="Erneut versuchen" Appearance="Secondary" HorizontalAlignment="Left"
Command="{Binding RetryCommand}" />
</StackPanel>
<TextBlock Text="Es wurden keine Geräte gefunden." Opacity="0.6"
@@ -53,10 +59,14 @@
<TextBlock Text="{Binding ErrorMessage}" Foreground="{StaticResource MikrotikRedBrush}"
TextWrapping="Wrap" FontSize="12"
Visibility="{Binding ErrorMessage, Converter={StaticResource StringToVisibilityConverter}}" />
<TextBlock Text="{Binding ActionHint}" Opacity="0.6"
TextWrapping="Wrap" FontSize="12" Margin="0,2,0,0"
Visibility="{Binding ActionHint, Converter={StaticResource StringToVisibilityConverter}}" />
</StackPanel>
<TextBlock Grid.Column="1" Text="{Binding StatusLabel}" Opacity="0.6" VerticalAlignment="Center" Margin="12,0" />
<ui:Button Grid.Column="2" Content="{Binding ActionLabel}" Appearance="Secondary"
IsEnabled="{Binding CanToggleStatic}"
ToolTip="{Binding ActionHint}"
Command="{Binding DataContext.ToggleStaticCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}, AncestorLevel=2}}"
CommandParameter="{Binding}" />
</Grid>