Files
OpenSquawk/app/composables/useRadioBackend.ts
leubeem c21ddc1d52 feat: live expected communication from backend, radio pronunciation toggle, and frequency validation
- Expected Communication card now driven entirely by backend responses:
  controller speech from controller_say_rendered, expected pilot phrase
  from expected_pilot_template — both update after every state transition
  instead of relying on the static COMMUNICATION_STEPS array
- Initial expected pilot phrase seeded on session creation via the new
  expected_pilot_template field in CreateSessionResponse, so the card
  shows the correct text before any transmission
- Radio pronunciation toggle (mdi-radio / mdi-text) on the card applies
  normalizeRadioPhrase() (ICAO alphabet, wun/too/tree, callsign expansion)
  to both ATC speech and expected pilot phrase
- Frequency validation at transmit time: if the pilot's active frequency
  does not match the state's expected frequency (resolved from
  frequency_name → flight-plan variable) a canned ATC reply is played
  and the backend is not called
- Flight-plan variables (callsign, squawk, destination, ATIS, SID, stand,
  initial altitude) now passed to createSession() as variable_overrides
  so backend sessions use real flight-plan data from the first state
- Backend session variables synced into local vars after each response
  so frontend and backend stay consistent
- Removed hardcoded frequency chip from Expected Communication card

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 17:51:44 +02:00

61 lines
1.9 KiB
TypeScript

export interface RadioSessionResponse {
session_id: string
flow_slug: string
current_state: string
variables: Record<string, any>
flags: Record<string, boolean>
expected_pilot_template: string | null
}
export interface RadioTransmitResponse {
session_id: string
next_state_id: string
controller_say_template: string | null
controller_say_rendered: string | null
expected_pilot_template: string | null
variables: Record<string, any>
flags: Record<string, boolean>
trace: Array<{ type: string; message: string }>
fallback_used: boolean
fallback_reason: string | null
auto_advanced_states?: string[]
}
export function useRadioBackend() {
const config = useRuntimeConfig()
function baseUrl(): string {
return (config.public.radioBackendUrl as string) || 'http://127.0.0.1:8000'
}
async function createSession(
flowSlug: string,
variables?: Record<string, any>,
): Promise<RadioSessionResponse> {
return await $fetch<RadioSessionResponse>(`${baseUrl()}/api/radio/session`, {
method: 'POST',
body: { flow_slug: flowSlug, variables: variables ?? null },
})
}
async function transmit(sessionId: string, pilotUtterance: string): Promise<RadioTransmitResponse> {
return await $fetch<RadioTransmitResponse>(
`${baseUrl()}/api/radio/session/${sessionId}/transmissions`,
{
method: 'POST',
body: { pilot_utterance: pilotUtterance },
},
)
}
async function deleteSession(sessionId: string): Promise<void> {
await $fetch(`${baseUrl()}/api/radio/session/${sessionId}`, { method: 'DELETE' })
}
async function fetchFlows(): Promise<any> {
return await $fetch(`${baseUrl()}/api/decision-flows/runtime`)
}
return { createSession, transmit, deleteSession, fetchFlows }
}