mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Sync time periodically
This commit is contained in:
@@ -18,6 +18,7 @@ from app.radio_sync import (
|
||||
stop_message_polling,
|
||||
stop_periodic_sync,
|
||||
sync_and_offload_all,
|
||||
sync_radio_time,
|
||||
)
|
||||
from app.routers import channels, contacts, health, messages, packets, radio, read_state, settings, ws
|
||||
|
||||
@@ -37,6 +38,9 @@ async def lifespan(app: FastAPI):
|
||||
if radio_manager.meshcore:
|
||||
register_event_handlers(radio_manager.meshcore)
|
||||
|
||||
# Sync radio clock with system time
|
||||
await sync_radio_time()
|
||||
|
||||
# Sync contacts/channels from radio to DB and clear radio
|
||||
logger.info("Syncing and offloading radio data...")
|
||||
result = await sync_and_offload_all()
|
||||
|
||||
@@ -332,6 +332,26 @@ async def stop_message_polling():
|
||||
logger.info("Stopped periodic message polling")
|
||||
|
||||
|
||||
async def sync_radio_time() -> bool:
|
||||
"""Sync the radio's clock with the system time.
|
||||
|
||||
Returns True if successful, False otherwise.
|
||||
"""
|
||||
mc = radio_manager.meshcore
|
||||
if not mc:
|
||||
logger.debug("Cannot sync time: radio not connected")
|
||||
return False
|
||||
|
||||
try:
|
||||
now = int(time.time())
|
||||
await mc.commands.set_time(now)
|
||||
logger.debug("Synced radio time to %d", now)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning("Failed to sync radio time: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
async def _periodic_sync_loop():
|
||||
"""Background task that periodically syncs and offloads."""
|
||||
while True:
|
||||
@@ -339,6 +359,7 @@ async def _periodic_sync_loop():
|
||||
await asyncio.sleep(SYNC_INTERVAL)
|
||||
logger.debug("Running periodic radio sync")
|
||||
await sync_and_offload_all()
|
||||
await sync_radio_time()
|
||||
except asyncio.CancelledError:
|
||||
logger.info("Periodic sync task cancelled")
|
||||
break
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from meshcore import EventType
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.dependencies import require_connected
|
||||
from app.radio_sync import sync_radio_time
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/radio", tags=["radio"])
|
||||
@@ -101,9 +101,7 @@ async def update_radio_config(update: RadioConfigUpdate) -> RadioConfigResponse:
|
||||
)
|
||||
|
||||
# Sync time with system clock
|
||||
now = int(time.time())
|
||||
logger.debug("Syncing radio time to %d", now)
|
||||
await mc.commands.set_time(now)
|
||||
await sync_radio_time()
|
||||
|
||||
return await get_radio_config()
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@ message polling from interfering with repeater CLI operations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from app.radio_sync import (
|
||||
_polling_pause_count,
|
||||
is_polling_paused,
|
||||
pause_polling,
|
||||
sync_radio_time,
|
||||
)
|
||||
|
||||
|
||||
@@ -113,3 +115,44 @@ class TestPollingPause:
|
||||
assert radio_sync._polling_pause_count == 1
|
||||
|
||||
assert radio_sync._polling_pause_count == 0
|
||||
|
||||
|
||||
class TestSyncRadioTime:
|
||||
"""Test the radio time sync function."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_not_connected(self):
|
||||
"""sync_radio_time returns False when radio is not connected."""
|
||||
with patch("app.radio_sync.radio_manager") as mock_manager:
|
||||
mock_manager.meshcore = None
|
||||
result = await sync_radio_time()
|
||||
assert result is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_true_on_success(self):
|
||||
"""sync_radio_time returns True when time is set successfully."""
|
||||
mock_mc = MagicMock()
|
||||
mock_mc.commands.set_time = AsyncMock()
|
||||
|
||||
with patch("app.radio_sync.radio_manager") as mock_manager:
|
||||
mock_manager.meshcore = mock_mc
|
||||
result = await sync_radio_time()
|
||||
|
||||
assert result is True
|
||||
mock_mc.commands.set_time.assert_called_once()
|
||||
# Verify timestamp is reasonable (within last few seconds)
|
||||
call_args = mock_mc.commands.set_time.call_args[0][0]
|
||||
import time
|
||||
assert abs(call_args - int(time.time())) < 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_on_exception(self):
|
||||
"""sync_radio_time returns False and doesn't raise on error."""
|
||||
mock_mc = MagicMock()
|
||||
mock_mc.commands.set_time = AsyncMock(side_effect=Exception("Radio error"))
|
||||
|
||||
with patch("app.radio_sync.radio_manager") as mock_manager:
|
||||
mock_manager.meshcore = mock_mc
|
||||
result = await sync_radio_time()
|
||||
|
||||
assert result is False
|
||||
|
||||
Reference in New Issue
Block a user