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>
Add an ArpScanner Default impl (30m/1m/10m, enabled) and mark the
arp_scanner field with serde default, so the [arp_scanner] section can
now be omitted entirely and fall back to code defaults — matching the
pattern used by the SNMP scanner. Previously these three durations were
mandatory and the backend would not start without them.
Reconcile the documentation to the canonical 30m/1m/10m: fix the README
NixOS example (was 15m/20m/30m) and options table (was 15m), and note
that the section is optional in both the README and sample TOML. The
NixOS module and sample TOML already used these values.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A typical router keeps active devices' ARP entries fresh continuously,
so a 10-minute poll interval stays well within common ARP cache TTLs
while keeping device-event churn modest (each poll records a DeviceSeen
event per device). A 5s per-request timeout adds margin for a busy
agent or large ARP table at no cost on the happy path.
Updates the default in settings.rs and the sample TOML, README and
NixOS module. TODO timing-review item narrowed to the ARP scanner.
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>
- Build the Flutter web app at release time (nix/frontend.nix) and bundle
it next to the binary at $out/share/oott/web; resolve it at runtime
relative to the executable. Remove the prebuilt backend/web from git.
- Add web_server.{ip_address,port,api_key} options to the NixOS module so
the generated config deserializes (was missing, causing a startup panic).
- Docker image: set SSL_CERT_FILE for outbound TLS, drop the heavy
nixos/nix base image, trim contents to [oott cacert], and ensure /tmp
exists.
- Remove the unused "nix" flake input and commit flake.lock.
- Provide Swagger UI to utoipa-swagger-ui offline via a pinned fetchurl so
the package builds in the Nix sandbox; skip the redundant check phase
(tests run via backend/run_tests.sh).
- sample_oott.toml: set database.path to /db/oott.db for the Docker image.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each scanner (ARP, mDNS, SSDP/UPnP, DHCP) can now be turned off via an
`enabled` flag in its config section, defaulting to true so existing
deployments are unchanged. A disabled scanner's entry function returns
early and never starts.
Documented in the README options table (also fixing the stale `timings.*`
key names to the actual `arp_scanner.*` keys), and added to the TOML
samples and the NixOS module.
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>
Add a Network ports section noting that the mDNS (UDP 5353) and SSDP
(UDP 1900) ports are fixed by their protocols and must be available on
the host, alongside the configurable web server port.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose the ssdp_scanner.probe_timeout option (default 2s, mirroring the
mDNS scanner) in the sample config and the NixOS service module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a new SSDP/UPnP network scanner alongside the existing ARP and
mDNS scanners:
- New scanner module under scanners/ssdp (finder, scanner, status)
- Wire the scanner into the main scan loop
- Add Ssdp variant to DeviceEventScanner
- Add SsdpScanner settings with a configurable probe timeout
- Expose /api/ssdp_scanner/status and wire it into OpenAPI generation
- Add a lint.sh helper and point CLAUDE.md at it
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make each Pushover alert triageable from a lock-screen preview: titles now
carry the device's identity (hostname, vendor, or MAC suffix) and a concise
verb, and bodies are sectioned (Device / Status / Activity or Changes) with
the registration line and device type so a user can decide whether to act
without opening the app. Adds a security hint on new-unregistered devices
and on vendor-change-on-same-MAC (a MAC-spoofing tell); IP-only changes
stay quiet since DHCP rotation is normal. Rendering moves into pure helpers
covered by unit tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extend PUT /api/devices/{mac} to accept an optional name and surface an
Edit action from both the device detail screen and the per-row overflow
menu, letting users modify owner, device type, vendor and name without
forgetting and re-registering.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scanners now call seen() (sighting semantics: stamp last_seen, preserve
registration, conditional vendor/device_type/name writes). update() is
the user-editable mutation (owner, device_type, vendor) wired to
PUT /api/devices/{mac_address}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Treat an empty->non-empty vendor transition as not a change, so first
deducing a vendor for a device that previously had none no longer raises
a "vendor changed" notification.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror the notifications list paging pattern (offset/limit, fetch one
extra row to detect the next page, First/Prev/Next controls) across the
devices DB query, REST endpoint, API client and UI. Also add a First
page button to the notifications pagination row.
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>
Move select_interface and MAC-resolution logic (resolve_mac_address,
parse_proc_net_arp, probe_mac) out of the device_finders modules into a
single utils::network utility so future scanners can reuse them. Rename
resolve to resolve_mac_address and make parse_proc_net_arp private.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce a second discovery module that passively listens for mDNS
multicast announcements (224.0.0.251:5353) and feeds discovered devices
into the existing devices/events/notifications pipeline, running in its
own task alongside the ARP scanner.
Since mDNS carries an IP and hostname but no MAC (the device key), the
module resolves IP->MAC via the OS ARP cache with a targeted ARP-probe
fallback. The advertised hostname is stored in a new optional `name`
column on devices (blank for ARP-only devices); the single `update`
writes `name` only when set so ARP rescans never clobber it. Device
names are included in event/notification messages.
Adds a GET /api/mdns_scanner/status endpoint (is_listening, devices_seen,
last_device_seen_seconds_ago) wired into OpenAPI, an optional
[mdns_scanner] config section, and the corresponding Nix module option.
Also aligns the Nix module's stale `timings` section with the current
`arp_scanner` schema.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Swap PagingController/PagedSliverList for explicit page state and
Previous/Next icon buttons below the list
- Fetch pageSize+1 items to detect the last page without a total-count
API; pagination controls are hidden when there is only one page
- Remove unused infinite_scroll_pagination dependency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If no interface is set in the config, the ARP scanner now picks the
first non-loopback, up, IPv4-enabled interface automatically. The
setting is optional in the TOML config, sample config, and Nix module.
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>
Replaces the old frontend-only `router` type and adds the full set used
by the backend: network_appliance, home_security, home_appliance, watch,
pc, gaming_console. Adds an `apiName` getter to map camelCase enum values
to the snake_case strings the API expects. Updates the unknown device icon
to a question mark.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Loads vendor-device-type.json at compile time (same pattern as mac-vendors-export.json)
and sets device_type when a new device is detected by the ARP scanner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Centralises all SnackBar calls behind UISnackbars.showError/showSuccess/
showWarning/showInfo, each themed via AppColorExtension. New warning and
info colour tokens added to both themes. Calling any method clears any
visible snackbar before showing the new one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a context menu to each device card with contextual actions:
- Unregistered devices show a Register option (dialog with owner field
and device type dropdown)
- Registered devices show a Forget option (confirmation dialog)
Also fixes CORS to allow PUT and DELETE methods, adds device type
tooltips on the icon, and renames the "New" filter/badge to
"Not registered".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add linter commands to CLAUDE.md
- Add clippy to Nix dev shell
- Add TODO.md with project task list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>