mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-14 13:26:06 +02:00
Phase 6: Radio config + path hash mode
This commit is contained in:
@@ -128,6 +128,8 @@ class RadioManager:
|
||||
self._setup_lock: asyncio.Lock | None = None
|
||||
self._setup_in_progress: bool = False
|
||||
self._setup_complete: bool = False
|
||||
self.path_hash_mode: int = 0
|
||||
self.path_hash_mode_supported: bool = False
|
||||
|
||||
async def _acquire_operation_lock(
|
||||
self,
|
||||
@@ -272,6 +274,22 @@ class RadioManager:
|
||||
"set_flood_scope failed (firmware may not support it): %s", exc
|
||||
)
|
||||
|
||||
# Query path hash mode support (best-effort; older firmware won't report it)
|
||||
try:
|
||||
device_query = await mc.commands.send_device_query()
|
||||
if device_query and "path_hash_mode" in device_query.payload:
|
||||
self.path_hash_mode = device_query.payload["path_hash_mode"]
|
||||
self.path_hash_mode_supported = True
|
||||
logger.info("Path hash mode: %d (supported)", self.path_hash_mode)
|
||||
else:
|
||||
self.path_hash_mode = 0
|
||||
self.path_hash_mode_supported = False
|
||||
logger.debug("Firmware does not report path_hash_mode")
|
||||
except Exception as exc:
|
||||
self.path_hash_mode = 0
|
||||
self.path_hash_mode_supported = False
|
||||
logger.debug("Failed to query path_hash_mode: %s", exc)
|
||||
|
||||
# Sync contacts/channels from radio to DB and clear radio
|
||||
logger.info("Syncing and offloading radio data...")
|
||||
result = await sync_and_offload_all(mc)
|
||||
@@ -412,6 +430,8 @@ class RadioManager:
|
||||
await self._meshcore.disconnect()
|
||||
self._meshcore = None
|
||||
self._setup_complete = False
|
||||
self.path_hash_mode = 0
|
||||
self.path_hash_mode_supported = False
|
||||
logger.debug("Radio disconnected")
|
||||
|
||||
async def reconnect(self, *, broadcast_on_success: bool = True) -> bool:
|
||||
|
||||
@@ -28,6 +28,12 @@ class RadioConfigResponse(BaseModel):
|
||||
tx_power: int = Field(description="Transmit power in dBm")
|
||||
max_tx_power: int = Field(description="Maximum transmit power in dBm")
|
||||
radio: RadioSettings
|
||||
path_hash_mode: int = Field(
|
||||
default=0, description="Path hash mode (0=1-byte, 1=2-byte, 2=3-byte)"
|
||||
)
|
||||
path_hash_mode_supported: bool = Field(
|
||||
default=False, description="Whether firmware supports path hash mode setting"
|
||||
)
|
||||
|
||||
|
||||
class RadioConfigUpdate(BaseModel):
|
||||
@@ -36,6 +42,9 @@ class RadioConfigUpdate(BaseModel):
|
||||
lon: float | None = None
|
||||
tx_power: int | None = Field(default=None, description="Transmit power in dBm")
|
||||
radio: RadioSettings | None = None
|
||||
path_hash_mode: int | None = Field(
|
||||
default=None, description="Path hash mode (0=1-byte, 1=2-byte, 2=3-byte)"
|
||||
)
|
||||
|
||||
|
||||
class PrivateKeyUpdate(BaseModel):
|
||||
@@ -64,6 +73,8 @@ async def get_radio_config() -> RadioConfigResponse:
|
||||
sf=info.get("radio_sf", 0),
|
||||
cr=info.get("radio_cr", 0),
|
||||
),
|
||||
path_hash_mode=radio_manager.path_hash_mode,
|
||||
path_hash_mode_supported=radio_manager.path_hash_mode_supported,
|
||||
)
|
||||
|
||||
|
||||
@@ -103,6 +114,15 @@ async def update_radio_config(update: RadioConfigUpdate) -> RadioConfigResponse:
|
||||
cr=update.radio.cr,
|
||||
)
|
||||
|
||||
if update.path_hash_mode is not None:
|
||||
if not radio_manager.path_hash_mode_supported:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Firmware does not support path hash mode setting"
|
||||
)
|
||||
logger.info("Setting path hash mode to %d", update.path_hash_mode)
|
||||
await mc.commands.set_path_hash_mode(update.path_hash_mode)
|
||||
radio_manager.path_hash_mode = update.path_hash_mode
|
||||
|
||||
# Sync time with system clock
|
||||
await sync_radio_time(mc)
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ export function SettingsRadioSection({
|
||||
const [bw, setBw] = useState('');
|
||||
const [sf, setSf] = useState('');
|
||||
const [cr, setCr] = useState('');
|
||||
const [pathHashMode, setPathHashMode] = useState('0');
|
||||
const [gettingLocation, setGettingLocation] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [rebooting, setRebooting] = useState(false);
|
||||
@@ -77,6 +78,7 @@ export function SettingsRadioSection({
|
||||
setBw(String(config.radio.bw));
|
||||
setSf(String(config.radio.sf));
|
||||
setCr(String(config.radio.cr));
|
||||
setPathHashMode(String(config.path_hash_mode));
|
||||
}, [config]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -159,6 +161,8 @@ export function SettingsRadioSection({
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsedPathHashMode = parseInt(pathHashMode, 10);
|
||||
|
||||
return {
|
||||
name,
|
||||
lat: parsedLat,
|
||||
@@ -170,6 +174,11 @@ export function SettingsRadioSection({
|
||||
sf: parsedSf,
|
||||
cr: parsedCr,
|
||||
},
|
||||
...(config.path_hash_mode_supported &&
|
||||
!isNaN(parsedPathHashMode) &&
|
||||
parsedPathHashMode !== config.path_hash_mode
|
||||
? { path_hash_mode: parsedPathHashMode }
|
||||
: {}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -427,6 +436,33 @@ export function SettingsRadioSection({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{config.path_hash_mode_supported && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="path-hash-mode">Path Hash Mode</Label>
|
||||
<select
|
||||
id="path-hash-mode"
|
||||
value={pathHashMode}
|
||||
onChange={(e) => setPathHashMode(e.target.value)}
|
||||
className="w-full h-10 px-3 rounded-md border border-input bg-background text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
||||
>
|
||||
<option value="0">1 byte (default)</option>
|
||||
<option value="1">2 bytes</option>
|
||||
<option value="2">3 bytes</option>
|
||||
</select>
|
||||
<div className="rounded-md border border-amber-500/50 bg-amber-500/10 p-3 text-xs text-amber-200">
|
||||
<p className="font-semibold mb-1">Compatibility Warning</p>
|
||||
<p>
|
||||
ALL nodes along a message's route — your radio, every repeater, and the
|
||||
recipient — must be running firmware that supports the selected mode. Messages
|
||||
sent with 2-byte or 3-byte hops will be dropped by any node on older firmware.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="text-sm text-destructive" role="alert">
|
||||
{error}
|
||||
|
||||
@@ -174,6 +174,8 @@ const baseConfig = {
|
||||
tx_power: 17,
|
||||
max_tx_power: 22,
|
||||
radio: { freq: 910.525, bw: 62.5, sf: 7, cr: 5 },
|
||||
path_hash_mode: 0,
|
||||
path_hash_mode_supported: false,
|
||||
};
|
||||
|
||||
const baseSettings = {
|
||||
|
||||
@@ -202,6 +202,8 @@ describe('App search jump target handling', () => {
|
||||
tx_power: 17,
|
||||
max_tx_power: 22,
|
||||
radio: { freq: 910.525, bw: 62.5, sf: 7, cr: 5 },
|
||||
path_hash_mode: 0,
|
||||
path_hash_mode_supported: false,
|
||||
});
|
||||
mocks.api.getSettings.mockResolvedValue({
|
||||
max_radio_contacts: 200,
|
||||
|
||||
@@ -158,6 +158,8 @@ describe('App startup hash resolution', () => {
|
||||
tx_power: 17,
|
||||
max_tx_power: 22,
|
||||
radio: { freq: 910.525, bw: 62.5, sf: 7, cr: 5 },
|
||||
path_hash_mode: 0,
|
||||
path_hash_mode_supported: false,
|
||||
});
|
||||
mocks.api.getSettings.mockResolvedValue({
|
||||
max_radio_contacts: 200,
|
||||
|
||||
@@ -41,6 +41,8 @@ function createConfig(overrides: Partial<RadioConfig> = {}): RadioConfig {
|
||||
tx_power: 10,
|
||||
max_tx_power: 20,
|
||||
radio: { freq: 915, bw: 250, sf: 10, cr: 8 },
|
||||
path_hash_mode: 0,
|
||||
path_hash_mode_supported: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ const baseConfig: RadioConfig = {
|
||||
sf: 7,
|
||||
cr: 5,
|
||||
},
|
||||
path_hash_mode: 0,
|
||||
path_hash_mode_supported: false,
|
||||
};
|
||||
|
||||
const baseHealth: HealthStatus = {
|
||||
|
||||
@@ -13,6 +13,8 @@ export interface RadioConfig {
|
||||
tx_power: number;
|
||||
max_tx_power: number;
|
||||
radio: RadioSettings;
|
||||
path_hash_mode: number;
|
||||
path_hash_mode_supported: boolean;
|
||||
}
|
||||
|
||||
export interface RadioConfigUpdate {
|
||||
@@ -21,6 +23,7 @@ export interface RadioConfigUpdate {
|
||||
lon?: number;
|
||||
tx_power?: number;
|
||||
radio?: RadioSettings;
|
||||
path_hash_mode?: number;
|
||||
}
|
||||
|
||||
export interface FanoutStatusEntry {
|
||||
|
||||
Reference in New Issue
Block a user