612280e924
Surface the same lifecycle timestamps on the Device Inventory cards as on the
detail panel. Tiles for devices placed on a canvas show their linked node's
created / last scan / last modified / last seen (correlated by ip or
ieee_address, aggregated across matches: created = oldest, others = newest).
Devices not yet on any canvas fall back to their discovered_at.
Rendered as compact relative times ("2d ago") with the full date on hover, in
a tight two-column footer so the tile keeps its original footprint.
Backend: PendingDeviceResponse gains node_created_at / node_last_scan /
node_last_modified / node_last_seen; the canvas correlation now also pulls node
timestamps in the same single query. Frontend: shared timeFormat util
(absolute + relative), reused by the detail panel.
ha-relevant: maybe
49 lines
1.4 KiB
Python
49 lines
1.4 KiB
Python
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class PendingDeviceResponse(BaseModel):
|
|
id: str
|
|
ip: str | None
|
|
mac: str | None
|
|
hostname: str | None
|
|
os: str | None
|
|
services: list[Any]
|
|
suggested_type: str | None
|
|
status: str
|
|
discovery_source: str | None
|
|
ieee_address: str | None = None
|
|
friendly_name: str | None = None
|
|
device_subtype: str | None = None
|
|
model: str | None = None
|
|
vendor: str | None = None
|
|
lqi: int | None = None
|
|
discovered_at: datetime
|
|
# Number of distinct canvases (designs) this device already appears on,
|
|
# correlated by ip / ieee_address against existing nodes. Computed per-request.
|
|
canvas_count: int = 0
|
|
# Timestamps from the linked canvas node(s), correlated by ip / ieee_address.
|
|
# Null when the device is not on any canvas yet. Aggregated across matches:
|
|
# created_at = oldest; last_scan / last_modified / last_seen = newest.
|
|
node_created_at: datetime | None = None
|
|
node_last_scan: datetime | None = None
|
|
node_last_modified: datetime | None = None
|
|
node_last_seen: datetime | None = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class ScanRunResponse(BaseModel):
|
|
id: str
|
|
status: str
|
|
kind: str = "ip"
|
|
ranges: list[str]
|
|
devices_found: int
|
|
started_at: datetime
|
|
finished_at: datetime | None
|
|
error: str | None
|
|
|
|
model_config = {"from_attributes": True}
|