fix: Add hw_model and missing fields to NodeHealth

- hw_model, neighbor_count, packets_sent_24h fields
- node_id_hex, battery_trend, packets_by_type, predicted_depletion_hours properties
- Populate hw_model from node data when creating NodeHealth
- Fixes reporter crash on node detail view
This commit is contained in:
K7ZVX 2026-05-05 04:26:52 +00:00
commit d8189e2e4d

View file

@ -95,7 +95,8 @@ class NodeHealth:
node_id: str node_id: str
short_name: str = "" short_name: str = ""
long_name: str = "" long_name: str = ""
role: str = "" role: str = ""
hw_model: str = ""
is_infrastructure: bool = False is_infrastructure: bool = False
last_seen: float = 0.0 last_seen: float = 0.0
is_online: bool = True is_online: bool = True
@ -116,7 +117,9 @@ class NodeHealth:
channel_utilization: Optional[float] = None # From device telemetry channel_utilization: Optional[float] = None # From device telemetry
air_util_tx: Optional[float] = None # From device telemetry air_util_tx: Optional[float] = None # From device telemetry
has_solar: bool = False has_solar: bool = False
uplink_enabled: bool = False uplink_enabled: bool = False
neighbor_count: int = 0
packets_sent_24h: int = 0
# Packet breakdown by portnum # Packet breakdown by portnum
packets_by_portnum: dict[str, int] = field(default_factory=dict) packets_by_portnum: dict[str, int] = field(default_factory=dict)
@ -143,6 +146,31 @@ class NodeHealth:
return 86400 / self.position_packet_count_24h return 86400 / self.position_packet_count_24h
return None return None
@property
def node_id_hex(self) -> str:
"""Return node_id in hex format with ! prefix."""
if self.node_id.startswith("!"):
return self.node_id
try:
return f"!{int(self.node_id):08x}"
except:
return self.node_id
@property
def battery_trend(self) -> str:
"""Return battery trend indicator."""
return "" # Not tracked yet
@property
def packets_by_type(self) -> dict:
"""Alias for packets_by_portnum."""
return self.packets_by_portnum
@property
def predicted_depletion_hours(self) -> Optional[float]:
"""Predict hours until battery depletion."""
return None # Not tracked yet
@dataclass @dataclass
class LocalityHealth: class LocalityHealth:
@ -317,7 +345,8 @@ class MeshHealthEngine:
# Extract fields (handle different API formats) # Extract fields (handle different API formats)
short_name = node.get("shortName") or node.get("short_name") or "" short_name = node.get("shortName") or node.get("short_name") or ""
long_name = node.get("longName") or node.get("long_name") or "" long_name = node.get("longName") or node.get("long_name") or ""
role = node.get("role") or node.get("hwModel") or "" role = node.get("role") or ""
hw_model = node.get("hwModel") or node.get("hw_model") or ""
# Determine if infrastructure # Determine if infrastructure
is_infra = str(role).upper() in INFRASTRUCTURE_ROLES is_infra = str(role).upper() in INFRASTRUCTURE_ROLES
@ -360,7 +389,8 @@ class MeshHealthEngine:
node_id=node_id, node_id=node_id,
short_name=short_name, short_name=short_name,
long_name=long_name, long_name=long_name,
role=role, role=role,
hw_model=hw_model,
is_infrastructure=is_infra, is_infrastructure=is_infra,
last_seen=last_seen, last_seen=last_seen,
is_online=is_online, is_online=is_online,