Replace the lifetime "devices seen since start" counter in the mDNS, SSDP
and DHCP scanners with a rolling count of distinct devices (deduped by
MAC) seen within the last hour.
Each scanner's status now tracks a MAC -> last-seen-time map; the snapshot
prunes entries older than 60 minutes and reports the remaining count. The
API field name (devices_seen) is unchanged, so only its meaning and the
frontend labels ("N devices in the last hour") are updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ARP and SNMP scanner status endpoints and front-end cards now expose
the number of devices found by the most recent successful scan, following
the existing mDNS device-count pattern. The count persists across the
running/waiting transitions, and for SNMP a failed poll keeps the last
good count rather than overwriting it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a sixth scanner that periodically queries an SNMP agent
(typically the router/firewall) for its ARP/neighbour cache via
SNMPv2c and feeds discovered devices into the shared devices, events
and notifications pipeline. Unlike the ARP scanner it generates no
traffic on the local segment and can surface devices across all
subnets the agent routes.
Scope is intentionally minimal: SNMPv2c only, a single target, and the
ipNetToMediaTable (ARP) only. SNMPv3 and switch MAC/forwarding-table
polling are left as follow-ups in TODO.md.
- backend: csnmp dependency; SnmpScanner config (opt-in, off unless a
[snmp_scanner] section is present); DeviceEventScanner::Snmp;
scanners/snmp/{finder,scanner,status}; main.rs wiring; status API
endpoint wired into OpenAPI
- frontend: SNMP scanner status model, card, API method, status screen
and summary card rows, and device-event label
- docs/config: sample TOML, README options, NixOS module option, and
setup notes for enabling SNMP on pfSense/OPNsense
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a DHCP scanner card to the Status screen and a DHCP row to the
consolidated Status card on the Home screen, mirroring the existing
mDNS/SSDP implementations. Also label DHCP-discovered device events as
"DHCP" instead of the raw "Dhcp".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface the SSDP/UPnP scanner alongside ARP and mDNS: a dedicated card on
the Status screen and a row in the consolidated Status card on Home. Mirrors
the existing mDNS implementation and reuses the generic ScannerStatusCard and
PolledValue infrastructure. Also maps the 'Ssdp' device-event scanner to the
"SSDP/UPnP" label.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract ScannerStatusCard<T> and PaginationBar so the ARP/mDNS cards and
the two paginated lists share one implementation each. Also fold the
four UISnackbars methods over a severity enum and fix Notification.toJson
serializing a method tear-off instead of the enum name.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a centralized BackendReachability singleton fed by a Dio
interceptor (connection-level failures only) and connectivity_plus.
PolledValue subscribes to it and pauses on offline / reloads
immediately on reconnect, replacing the previous per-card error
cascade. MainShell renders a global OfflineBanner with Retry and
Settings actions; cards downgrade error→stale when offline so the
banner is the only red element.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PolledValue gains pause()/resume() methods. ScannersStatusCard and
NotificationsList now implement RouteAware (didPushNext/didPopNext)
and WidgetsBindingObserver to stop API calls and tick timers when
the widget is not visible, resuming with an immediate fetch on return.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The four polling status cards (ARP, mDNS, scanners summary, devices
summary) each rolled their own Timer/_status/_error/_isLoading state and
flipped to a red 'Error' UI on the first failed poll, throwing away
last-known-good data that was still in memory. A momentary backend blip
made every card flash red.
Introduce PolledValue<T>, a ChangeNotifier that owns the periodic timer,
in-flight CancelToken, last value, last error, and a freshness
classification (initialLoading / fresh / stale / error). 'stale' kicks in
on the first failure but keeps the value visible; 'error' only takes over
after staleErrorAfter elapses without a success (30s for the 5s scanner
polls, 3min for the 1min device-summary poll). All four cards now branch
on freshness and show a small live-updating warning icon with a tooltip
('Last updated N ago — <error>') while stale, instead of flipping red.
Also threads CancelToken? through the three polled GET endpoints.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Set 5s/15s/15s connect/receive/send timeouts so calls fail fast instead of
hanging. Introduce dioErrorToUserMessage() and route DioException-throwing
call sites (device actions, devices list, notifications list, scanners
status card) through it, replacing raw e.toString() output. Notifications
list now surfaces load errors inline in the theme's error color, matching
the devices list pattern. Status dots show the mapped detail as a tooltip.
Stop logging the API key in cleartext.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add an mDNS Scanner status card to the Status screen mirroring the ARP
card, and replace the ARP card on the Home screen with a combined Status
card that summarizes both scanners in one line each. Extract the shared
duration formatting helper into utils/duration_formatter.dart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DeviceSummaryCard and ArpScannerCard are now tappable, navigating to
the devices list and status screen respectively
- Elevated "Devices" title to titleLarge; section subtitles promoted to
bodyMedium/w600 while row content is demoted to bodySmall
- Renamed top-level row label to "Registered in the system"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Convert ArpScannerCard to a self-managing StatefulWidget (owns its own 5s refresh + 1s tick timers), removing the duplicated state management that existed in both HomeScreen and StatusScreen
- Extract NotificationCard to home/notification_card.dart
- Extract DeviceSummaryCard (with its 1-min refresh timer) to widgets/device_summary_card.dart
- HomeScreen drops from 515 to 253 lines; StatusScreen from 82 to 19 lines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a responsive Home screen combining the notification list with a
device summary panel and ARP scanner status, visible side-by-side on
wide screens and stacked on narrow ones.
Backend:
- New GET /api/devices/summary endpoint returning registered device
counts and "seen in last 24h / 7 days" breakdowns by registration
status
- Migration 07 adds indexes on (is_registered) and (last_seen,
is_registered) so summary queries use index range scans instead of
full table scans
Frontend:
- HomeScreen replaces NotificationList, embedding notifications,
device summary card, and ArpScannerCard in a LayoutBuilder-driven
two-column (≥700 px) or single-column layout
- ArpScannerCard extracted to a shared public widget reused by both
HomeScreen and StatusScreen
- Nav rail entry renamed to "Home" with home icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lists devices with New/Registered/All filtering, pull-to-refresh,
device-type icons, and visual differentiation between new and registered
devices. Introduces a reusable StatusBadge widget for colour-coded labels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>