mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-01 11:02:56 +02:00
Show node name if we find it in the DB already. Closes #128.
This commit is contained in:
@@ -681,6 +681,10 @@ class RadioDiscoveryResult(BaseModel):
|
||||
"""One mesh node heard during a discovery sweep."""
|
||||
|
||||
public_key: str = Field(description="Discovered node public key as hex")
|
||||
name: str | None = Field(
|
||||
default=None,
|
||||
description="Known name for this node from contacts DB, if any",
|
||||
)
|
||||
node_type: Literal["repeater", "sensor"] = Field(description="Discovered node class")
|
||||
heard_count: int = Field(default=1, description="How many responses were heard from this node")
|
||||
local_snr: float | None = Field(
|
||||
|
||||
@@ -18,6 +18,7 @@ from app.models import (
|
||||
from app.radio_sync import send_advertisement as do_send_advertisement
|
||||
from app.radio_sync import sync_radio_time
|
||||
from app.repository import ContactRepository
|
||||
from app.services.contact_reconciliation import promote_prefix_contacts_for_contact
|
||||
from app.services.radio_commands import (
|
||||
KeystoreRefreshError,
|
||||
PathHashModeUnsupportedError,
|
||||
@@ -197,9 +198,23 @@ async def _persist_new_discovery_contacts(results: list[RadioDiscoveryResult]) -
|
||||
on_radio=False,
|
||||
)
|
||||
await ContactRepository.upsert(contact)
|
||||
promoted_keys = await promote_prefix_contacts_for_contact(
|
||||
public_key=result.public_key,
|
||||
log=logger,
|
||||
)
|
||||
created = await ContactRepository.get_by_key(result.public_key)
|
||||
if created is not None:
|
||||
broadcast_event("contact", created.model_dump())
|
||||
for old_key in promoted_keys:
|
||||
broadcast_event("contact_deleted", {"public_key": old_key})
|
||||
|
||||
|
||||
async def _attach_known_names(results: list[RadioDiscoveryResult]) -> None:
|
||||
"""Resolve known contact names for discovery results from the DB."""
|
||||
for result in results:
|
||||
contact = await ContactRepository.get_by_key(result.public_key)
|
||||
if contact is not None and contact.name:
|
||||
result.name = contact.name
|
||||
|
||||
|
||||
@router.get("/config", response_model=RadioConfigResponse)
|
||||
@@ -365,6 +380,7 @@ async def discover_mesh(request: RadioDiscoveryRequest) -> RadioDiscoveryRespons
|
||||
),
|
||||
)
|
||||
await _persist_new_discovery_contacts(results)
|
||||
await _attach_known_names(results)
|
||||
return RadioDiscoveryResponse(
|
||||
target=request.target,
|
||||
duration_seconds=DISCOVERY_WINDOW_SECONDS,
|
||||
|
||||
@@ -846,11 +846,16 @@ export function SettingsRadioSection({
|
||||
className="rounded-md border border-input bg-background px-3 py-2"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm font-medium capitalize">{result.node_type}</span>
|
||||
<span className="text-sm font-medium">
|
||||
{result.name ?? <span className="capitalize">{result.node_type}</span>}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
heard {result.heard_count} time{result.heard_count === 1 ? '' : 's'}
|
||||
</span>
|
||||
</div>
|
||||
{result.name && (
|
||||
<p className="text-xs capitalize text-muted-foreground">{result.node_type}</p>
|
||||
)}
|
||||
<p className="mt-1 break-all font-mono text-xs text-muted-foreground">
|
||||
{result.public_key}
|
||||
</p>
|
||||
|
||||
@@ -300,6 +300,7 @@ describe('SettingsModal', () => {
|
||||
results: [
|
||||
{
|
||||
public_key: '11'.repeat(32),
|
||||
name: null,
|
||||
node_type: 'repeater',
|
||||
heard_count: 2,
|
||||
local_snr: 7.5,
|
||||
|
||||
@@ -34,6 +34,7 @@ export type RadioDiscoveryTarget = 'repeaters' | 'sensors' | 'all';
|
||||
|
||||
export interface RadioDiscoveryResult {
|
||||
public_key: string;
|
||||
name: string | null;
|
||||
node_type: 'repeater' | 'sensor';
|
||||
heard_count: number;
|
||||
local_snr: number | null;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import ANY, AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
@@ -375,6 +375,11 @@ class TestDiscoverMesh:
|
||||
return_value=None,
|
||||
),
|
||||
patch("app.routers.radio.ContactRepository.upsert", new_callable=AsyncMock),
|
||||
patch(
|
||||
"app.routers.radio.promote_prefix_contacts_for_contact",
|
||||
new_callable=AsyncMock,
|
||||
return_value=[],
|
||||
),
|
||||
patch("app.routers.radio.broadcast_event"),
|
||||
):
|
||||
response = await discover_mesh(RadioDiscoveryRequest(target="repeaters"))
|
||||
@@ -436,18 +441,27 @@ class TestDiscoverMesh:
|
||||
patch(
|
||||
"app.routers.radio.ContactRepository.get_by_key",
|
||||
new_callable=AsyncMock,
|
||||
side_effect=[None, created_contact],
|
||||
# 1st: _persist check (not found), 2nd: _persist re-fetch (created),
|
||||
# 3rd: _attach_known_names lookup
|
||||
side_effect=[None, created_contact, created_contact],
|
||||
) as mock_get_by_key,
|
||||
patch(
|
||||
"app.routers.radio.ContactRepository.upsert", new_callable=AsyncMock
|
||||
) as mock_upsert,
|
||||
patch(
|
||||
"app.routers.radio.promote_prefix_contacts_for_contact",
|
||||
new_callable=AsyncMock,
|
||||
return_value=[],
|
||||
) as mock_promote,
|
||||
patch("app.routers.radio.broadcast_event") as mock_broadcast,
|
||||
):
|
||||
response = await discover_mesh(RadioDiscoveryRequest(target="repeaters"))
|
||||
|
||||
assert len(response.results) == 1
|
||||
assert response.results[0].name is None # created_contact has no name
|
||||
mock_get_by_key.assert_awaited()
|
||||
mock_upsert.assert_awaited_once()
|
||||
mock_promote.assert_awaited_once_with(public_key="44" * 32, log=ANY)
|
||||
upsert_arg = mock_upsert.await_args.args[0]
|
||||
assert upsert_arg.public_key == "44" * 32
|
||||
assert upsert_arg.type == 2
|
||||
@@ -542,6 +556,11 @@ class TestDiscoverMesh:
|
||||
return_value=None,
|
||||
),
|
||||
patch("app.routers.radio.ContactRepository.upsert", new_callable=AsyncMock),
|
||||
patch(
|
||||
"app.routers.radio.promote_prefix_contacts_for_contact",
|
||||
new_callable=AsyncMock,
|
||||
return_value=[],
|
||||
),
|
||||
patch("app.routers.radio.broadcast_event"),
|
||||
):
|
||||
response = await discover_mesh(RadioDiscoveryRequest(target="all"))
|
||||
|
||||
Reference in New Issue
Block a user