Documentation
¶
Overview ¶
Package client is the TUI's network layer. It wraps the daemon's HTTP REST + SSE surfaces in typed methods so the bubbletea model can stay focused on rendering.
DTO shapes mirror internal/api/types.go and internal/storage's CallRow, copied by value rather than imported so that the TUI stays a pure client of the wire protocol — `internal/tui` doesn't pull in the server-side packages.
Index ¶
- func FormatFreqMHz(hz uint32) string
- type ActiveCallDTO
- type AudioStatusDTO
- type CallRow
- type Client
- func (c *Client) ActiveCalls(ctx context.Context) ([]ActiveCallDTO, error)
- func (c *Client) AudioStatus(ctx context.Context) (AudioStatusDTO, error)
- func (c *Client) Base() string
- func (c *Client) Devices(ctx context.Context) ([]SDRStatus, error)
- func (c *Client) EndCall(ctx context.Context, deviceSerial, reason string) error
- func (c *Client) HTTPClient() *http.Client
- func (c *Client) Health(ctx context.Context) (Health, error)
- func (c *Client) History(ctx context.Context, f HistoryFilter) ([]CallRow, error)
- func (c *Client) Metrics(ctx context.Context) (map[string]float64, error)
- func (c *Client) MutationStatus(ctx context.Context) (MutationStatus, error)
- func (c *Client) ResetToneDevice(ctx context.Context, serial string) error
- func (c *Client) Runtime(ctx context.Context) (RuntimeDTO, error)
- func (c *Client) Scanner(ctx context.Context) (ScannerStatusDTO, error)
- func (c *Client) ScannerClearManualTune(ctx context.Context, index int) error
- func (c *Client) ScannerConvDwell(ctx context.Context, index int) error
- func (c *Client) ScannerConvHold(ctx context.Context) error
- func (c *Client) ScannerConvLockout(ctx context.Context, index int) error
- func (c *Client) ScannerConvResume(ctx context.Context) error
- func (c *Client) ScannerConvUnlockout(ctx context.Context, index int) error
- func (c *Client) ScannerHuntHold(ctx context.Context, system string) error
- func (c *Client) ScannerHuntResume(ctx context.Context, system string) error
- func (c *Client) ScannerHuntRetune(ctx context.Context, system string) error
- func (c *Client) ScannerManualTune(ctx context.Context, freqHz uint32, label, mode string) (int, error)
- func (c *Client) ScannerSetMode(ctx context.Context, mode string) error
- func (c *Client) SetAudio(ctx context.Context, volume *float32, muted *bool, recording *bool) (AudioStatusDTO, error)
- func (c *Client) SetToken(t string)
- func (c *Client) SetTokenFile(path string) error
- func (c *Client) Stream(ctx context.Context) (<-chan Event, <-chan error)
- func (c *Client) SweepRetention(ctx context.Context) error
- func (c *Client) System(ctx context.Context, name string) (SystemDTO, error)
- func (c *Client) Systems(ctx context.Context) ([]SystemDTO, error)
- func (c *Client) Talkgroup(ctx context.Context, id uint32) (TalkgroupDTO, error)
- func (c *Client) Talkgroups(ctx context.Context) ([]TalkgroupDTO, error)
- func (c *Client) UpdateTalkgroup(ctx context.Context, id uint32, priority *int, lockout *bool, scan *bool) (TalkgroupDTO, error)
- func (c *Client) Version(ctx context.Context) (string, error)
- type ConvChannelStatusDTO
- type ConvScannerStatusDTO
- type Event
- type GrantDTO
- type HTTPError
- type Health
- type HistoryFilter
- type LockState
- type MutationStatus
- type RuntimeDTO
- type SDRStatus
- type ScannerStatusDTO
- type SystemDTO
- type SystemHuntStatusDTO
- type TalkgroupDTO
- type Tone
- type ToneProfileDTO
- type Version
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FormatFreqMHz ¶
FormatFreqMHz formats a frequency in Hz as "851.012500 MHz" with six decimal places. Zero returns "—".
Types ¶
type ActiveCallDTO ¶
type ActiveCallDTO struct {
Grant GrantDTO `json:"grant"`
Talkgroup *TalkgroupDTO `json:"talkgroup,omitempty"`
DeviceSerial string `json:"device_serial"`
StartedAt time.Time `json:"started_at"`
LastHeardAt time.Time `json:"last_heard_at"`
}
ActiveCallDTO mirrors api.ActiveCallDTO.
type AudioStatusDTO ¶
type AudioStatusDTO struct {
BackendEnabled bool `json:"backend_enabled"`
SampleRate uint32 `json:"sample_rate"`
Volume float32 `json:"volume"`
Muted bool `json:"muted"`
RecordingEnabled bool `json:"recording_enabled"`
DropsTotal uint64 `json:"drops_total"`
}
AudioStatusDTO mirrors api.AudioStatusDTO for the wire layer.
type CallRow ¶
type CallRow struct {
ID int64 `json:"id"`
System string `json:"system"`
Protocol string `json:"protocol"`
GroupID uint32 `json:"group_id"`
SourceID uint32 `json:"source_id"`
FrequencyHz uint32 `json:"frequency_hz"`
Encrypted bool `json:"encrypted"`
Emergency bool `json:"emergency"`
DataCall bool `json:"data_call"`
DeviceSerial string `json:"device_serial"`
StartedAt time.Time `json:"started_at"`
EndedAt time.Time `json:"ended_at,omitempty"`
DurationMs int64 `json:"duration_ms,omitempty"`
EndReason string `json:"end_reason,omitempty"`
TalkgroupAlpha string `json:"talkgroup_alpha,omitempty"`
}
CallRow mirrors storage.CallRow — the shape returned by GET /api/v1/calls/history.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a typed HTTP client for the daemon's read API.
The base URL is normalised to drop any trailing slash; methods build paths relative to it.
func New ¶
New constructs a Client. timeout applies per request; the SSE stream uses its own context-bounded request that ignores this timeout (a long-lived stream is the normal case for SSE).
insecure disables TLS certificate verification — useful when pointing at a self-signed daemon over HTTPS in a lab.
func (*Client) ActiveCalls ¶
func (c *Client) ActiveCalls(ctx context.Context) ([]ActiveCallDTO, error)
ActiveCalls calls GET /api/v1/calls/active.
func (*Client) AudioStatus ¶
func (c *Client) AudioStatus(ctx context.Context) (AudioStatusDTO, error)
AudioStatus calls GET /api/v1/audio. Returns a zero value (no error) when the daemon doesn't have the audio cockpit wired so older daemons don't break the TUI.
func (*Client) Base ¶
Base returns the daemon base URL the client is pointed at. Used in the TUI's status bar.
func (*Client) EndCall ¶
EndCall calls POST /api/v1/calls/{deviceSerial}/end. reason is optional; defaults to "manual".
func (*Client) HTTPClient ¶
HTTPClient returns the underlying http.Client so the SSE reader can issue a long-lived request without the per-call timeout.
func (*Client) Metrics ¶
Metrics calls GET /metrics, parses Prometheus text-format output, and returns a curated map of name → most recent sample value. We don't try to be a full Prometheus parser — just enough to fuel the Metrics panel's small set of named series.
func (*Client) MutationStatus ¶
func (c *Client) MutationStatus(ctx context.Context) (MutationStatus, error)
MutationStatus calls GET /api/v1/mutations. Returns a zero-value status (and a nil error) if the daemon doesn't know the route.
func (*Client) ResetToneDevice ¶
ResetToneDevice calls POST /api/v1/devices/{serial}/tone-reset.
func (*Client) Runtime ¶
func (c *Client) Runtime(ctx context.Context) (RuntimeDTO, error)
Runtime calls GET /api/v1/runtime — the read-only daemon config snapshot consumed by the TUI's tabbed Settings inspector.
func (*Client) Scanner ¶
func (c *Client) Scanner(ctx context.Context) (ScannerStatusDTO, error)
Scanner calls GET /api/v1/scanner. Always returns 200 even when no scanner subsystem is wired (an empty ScannerStatusDTO).
func (*Client) ScannerClearManualTune ¶
ScannerClearManualTune calls DELETE /api/v1/scanner/manual_tune/{index}.
func (*Client) ScannerConvDwell ¶
func (*Client) ScannerConvHold ¶
ScannerConvHold / ScannerConvResume / ScannerConvDwell drive the conventional FM scanner.
func (*Client) ScannerConvLockout ¶
ScannerConvLockout / ScannerConvUnlockout toggle the per-channel lockout the scanner respects when picking the next channel to dwell on. The flag is runtime-only; it doesn't persist across daemon restarts.
func (*Client) ScannerConvUnlockout ¶
func (*Client) ScannerHuntHold ¶
ScannerHuntHold / ScannerHuntResume / ScannerHuntRetune call the per-system hunt mutation endpoints. system must match a configured trunked system name.
func (*Client) ScannerHuntResume ¶
func (*Client) ScannerHuntRetune ¶
func (*Client) ScannerManualTune ¶
func (c *Client) ScannerManualTune(ctx context.Context, freqHz uint32, label, mode string) (int, error)
ScannerManualTune calls POST /api/v1/scanner/manual_tune. The optional label / mode / squelch_dbfs default on the server side when empty; only frequency_hz is required.
func (*Client) ScannerSetMode ¶
ScannerSetMode calls PATCH /api/v1/scanner with the new global scan_mode ("all" or "list").
func (*Client) SetAudio ¶
func (c *Client) SetAudio(ctx context.Context, volume *float32, muted *bool, recording *bool) (AudioStatusDTO, error)
SetAudio calls PATCH /api/v1/audio with whichever knobs are non-nil. Pass nil to leave a field unchanged.
func (*Client) SetToken ¶
SetToken sets an inline Bearer token. Empty disables the header. Safe to call after construction; takes effect on the next request.
func (*Client) SetTokenFile ¶
SetTokenFile registers a file path the client re-reads on every request to pick up daemon-side rotation. Empty disables file-based tokens. Sets the in-memory token from an initial read; returns the error from that read so the caller can surface it at startup.
func (*Client) Stream ¶
Stream opens GET /api/v1/events as Server-Sent Events and returns a channel of decoded Events plus an error channel that receives at most one terminal error before being closed.
The stream lives until ctx is cancelled or the server closes the connection. Reconnection is the caller's responsibility — the bubbletea Update loop owns the retry policy.
SSE framing reference: dispatch on blank line, accumulate `event:` and `data:` fields. Comments (lines starting with `:`) are ignored. Multi-line data fields are joined with `\n` per the W3C spec; the daemon currently emits single-line `data:` so we honour the spec without exercising it heavily.
func (*Client) SweepRetention ¶
SweepRetention calls POST /api/v1/retention/sweep.
func (*Client) System ¶
System calls GET /api/v1/systems/{name} and returns the detail record for one system. Used by the TUI's drill-in modal.
func (*Client) Talkgroup ¶
Talkgroup calls GET /api/v1/talkgroups/{id} and returns the detail record for one talkgroup. Used by the TUI's drill-in modal.
func (*Client) Talkgroups ¶
func (c *Client) Talkgroups(ctx context.Context) ([]TalkgroupDTO, error)
Talkgroups calls GET /api/v1/talkgroups.
type ConvChannelStatusDTO ¶
type ConvChannelStatusDTO struct {
Index int `json:"index"`
Label string `json:"label"`
FrequencyHz uint32 `json:"frequency_hz"`
Mode string `json:"mode"`
Active bool `json:"active"`
LockedOut bool `json:"locked_out,omitempty"`
LastBreakAt time.Time `json:"last_break_at,omitempty"`
}
ConvChannelStatusDTO mirrors api.ConvChannelStatusDTO.
type ConvScannerStatusDTO ¶
type ConvScannerStatusDTO struct {
Enabled bool `json:"enabled"`
State string `json:"state,omitempty"`
DeviceSerial string `json:"device_serial,omitempty"`
CursorIndex int `json:"cursor_index,omitempty"`
Channels []ConvChannelStatusDTO `json:"channels"`
}
ConvScannerStatusDTO mirrors api.ConvScannerStatusDTO.
type Event ¶
type Event struct {
Kind string
Time time.Time
Raw json.RawMessage
}
Event is one decoded SSE event. Kind matches the events.Kind constants ("cc.locked", "grant", "call.start", …); Time is the envelope timestamp; Raw is the un-decoded JSON of the kind- specific payload, which the caller can decode into a typed payload as needed (Grant, Active, Tone, …).
type GrantDTO ¶
type GrantDTO struct {
System string `json:"system"`
Protocol string `json:"protocol"`
GroupID uint32 `json:"group_id"`
SourceID uint32 `json:"source_id"`
FrequencyHz uint32 `json:"frequency_hz"`
ChannelID uint8 `json:"channel_id,omitempty"`
ChannelNumber uint16 `json:"channel_number,omitempty"`
Encrypted bool `json:"encrypted,omitempty"`
Emergency bool `json:"emergency,omitempty"`
DataCall bool `json:"data_call,omitempty"`
}
GrantDTO mirrors api.GrantDTO.
type HistoryFilter ¶
type HistoryFilter struct {
System string
GroupID uint32
Since time.Time
Until time.Time
Limit int // 0 → server default (100)
OnlyEnded bool
}
HistoryFilter is the query-parameter shape for GET /api/v1/calls/history. Zero-valued fields are omitted from the outgoing query string.
type LockState ¶
type LockState struct {
FrequencyHz uint32 `json:"FrequencyHz"`
NAC uint16 `json:"NAC,omitempty"`
SystemID uint32 `json:"SystemID,omitempty"`
Repeater string `json:"Repeater,omitempty"`
MCC uint16 `json:"MCC,omitempty"`
MNC uint16 `json:"MNC,omitempty"`
}
LockState is the payload of cc.locked / cc.lost events. FrequencyHz is always present; the other fields are protocol- specific.
type MutationStatus ¶
type MutationStatus struct {
AllowMutations bool `json:"allow_mutations"`
EngineWritable bool `json:"engine_writable"`
RetentionWritable bool `json:"retention_writable"`
TonesWritable bool `json:"tones_writable"`
}
MutationStatus mirrors GET /api/v1/mutations. The TUI fetches it once at startup to decide which write keybindings to expose. A daemon that doesn't know the route (older build) returns 404 and the TUI treats that as "no mutations".
type RuntimeDTO ¶
type RuntimeDTO struct {
HTTPAddr string `json:"http_addr,omitempty"`
GRPCAddr string `json:"grpc_addr,omitempty"`
WSPath string `json:"ws_path,omitempty"`
SSEPath string `json:"sse_path,omitempty"`
MetricsPath string `json:"metrics_path,omitempty"`
AllowMutations bool `json:"allow_mutations"`
LogLevel string `json:"log_level"`
LogFormat string `json:"log_format"`
Version string `json:"version,omitempty"`
StorageDBPath string `json:"storage_db_path,omitempty"`
StorageCCCache string `json:"storage_cc_cache,omitempty"`
RetentionCallLogDays int `json:"retention_call_log_days"`
RetentionFilesDays int `json:"retention_files_days"`
RetentionInterval time.Duration `json:"retention_interval_ns"`
RecordingDir string `json:"recording_dir,omitempty"`
RecordingSampleRate int `json:"recording_sample_rate"`
RecordingWriteRaw bool `json:"recording_write_raw"`
RecordingEQEnabled bool `json:"recording_eq_enabled"`
RecordingEQTaps int `json:"recording_eq_taps,omitempty"`
RecordingEQStepSize string `json:"recording_eq_step_size,omitempty"`
AudioEnabled bool `json:"audio_enabled"`
AudioDevice string `json:"audio_device,omitempty"`
AudioSampleRate int `json:"audio_sample_rate"`
AudioBufferMs int `json:"audio_buffer_ms"`
AudioBackends []string `json:"audio_backends"`
AudioDisableFallbk bool `json:"audio_disable_fallback"`
SDRSampleRate int `json:"sdr_sample_rate"`
SDRBackends []string `json:"sdr_backends"`
ScannerScanMode string `json:"scanner_scan_mode"`
ScannerCCHuntEnabled bool `json:"scanner_cc_hunt_enabled"`
ScannerCCHuntDwellMs int `json:"scanner_cc_hunt_dwell_ms"`
ScannerCCHuntBackoffMs int `json:"scanner_cc_hunt_backoff_ms"`
ScannerCCMaxBackoffMs int `json:"scanner_cc_max_backoff_ms"`
ScannerManualTuneEnabled bool `json:"scanner_manual_tune_enabled"`
ToneProfiles []ToneProfileDTO `json:"tone_profiles,omitempty"`
VocoderMap map[string]string `json:"vocoder_map"`
MetricsEnabled bool `json:"metrics_enabled"`
}
HTTPError is returned by the REST methods on a non-2xx response. Status carries the HTTP code; Body carries up to a few hundred bytes of the response for the toast UI to show. RuntimeDTO mirrors api.RuntimeDTO. Consumed by the TUI's tabbed Settings inspector to render every configured feature, output, protocol surface, etc. — the touch-point inventory the operator expects to see in one place.
type SDRStatus ¶
type SDRStatus struct {
Driver string `json:"driver"`
Serial string `json:"serial"`
Manufacturer string `json:"manufacturer,omitempty"`
Product string `json:"product,omitempty"`
TunerName string `json:"tuner_name,omitempty"`
Role string `json:"role"`
Attached bool `json:"attached"`
GainTenthDB int `json:"gain_tenth_db"`
GainAuto bool `json:"gain_auto"`
PPM int `json:"ppm"`
BiasTee bool `json:"bias_tee"`
Gains []int `json:"gains,omitempty"`
}
SDRStatus mirrors api.sdr.SDRStatus — the per-device payload returned by GET /api/v1/devices and embedded in sdr.attached / sdr.detached SSE events.
type ScannerStatusDTO ¶
type ScannerStatusDTO struct {
ScanMode string `json:"scan_mode"`
Systems []SystemHuntStatusDTO `json:"systems"`
Conventional ConvScannerStatusDTO `json:"conventional"`
TalkgroupScanCount int `json:"tg_scan_count"`
TalkgroupTotalCount int `json:"tg_total"`
}
ScannerStatusDTO mirrors api.ScannerStatus — the unified scanner snapshot returned by GET /api/v1/scanner. Fields are zero-valued when the underlying subsystem isn't wired (e.g. no CC hunter → empty Systems list).
type SystemDTO ¶
type SystemDTO struct {
Name string `json:"name"`
Protocol string `json:"protocol"`
ControlChannels []uint32 `json:"control_channels"`
WACN uint32 `json:"wacn,omitempty"`
SystemID uint16 `json:"system_id,omitempty"`
RFSS uint8 `json:"rfss,omitempty"`
Site uint8 `json:"site,omitempty"`
// Per-protocol FEC opt-out surface (mirrors api.SystemDTO).
// The TUI Settings panel renders these.
TETRAColourCode uint32 `json:"tetra_colour_code,omitempty"`
TETRAChannel string `json:"tetra_channel,omitempty"`
TETRAChannelCoding string `json:"tetra_channel_coding,omitempty"`
LTRFCSMode string `json:"ltr_fcs_mode,omitempty"`
LTRManchesterMode string `json:"ltr_manchester_mode,omitempty"`
P25Phase2TrellisMode string `json:"p25_phase2_trellis_mode,omitempty"`
P25Phase2RSMode string `json:"p25_phase2_rs_mode,omitempty"`
P25Phase2ScramblerMode string `json:"p25_phase2_scrambler_mode,omitempty"`
NXDNViterbiMode string `json:"nxdn_viterbi_mode,omitempty"`
EDACSBCHMode string `json:"edacs_bch_mode,omitempty"`
MPT1327BCHMode string `json:"mpt1327_bch_mode,omitempty"`
MPT1327CWSCTolerance string `json:"mpt1327_cwsc_tolerance,omitempty"`
MotorolaBCHMode string `json:"motorola_bch_mode,omitempty"`
}
SystemDTO mirrors api.SystemDTO.
type SystemHuntStatusDTO ¶
type SystemHuntStatusDTO struct {
Name string `json:"name"`
Protocol string `json:"protocol"`
State string `json:"state"`
AttemptedFreqHz uint32 `json:"attempted_freq_hz,omitempty"`
AttemptIndex int `json:"attempt_index,omitempty"`
TotalCandidates int `json:"total_candidates,omitempty"`
LockedFreqHz uint32 `json:"locked_freq_hz,omitempty"`
LockedAt time.Time `json:"locked_at,omitempty"`
NAC uint16 `json:"nac,omitempty"`
LastFailedAt time.Time `json:"last_failed_at,omitempty"`
BackoffMs int `json:"backoff_ms,omitempty"`
LastGrantAt time.Time `json:"last_grant_at,omitempty"`
}
SystemHuntStatusDTO mirrors api.SystemHuntStatusDTO.
type TalkgroupDTO ¶
type TalkgroupDTO struct {
ID uint32 `json:"id"`
AlphaTag string `json:"alpha_tag"`
Description string `json:"description,omitempty"`
Tag string `json:"tag,omitempty"`
Group string `json:"group,omitempty"`
Mode string `json:"mode,omitempty"`
Priority int `json:"priority,omitempty"`
Lockout bool `json:"lockout,omitempty"`
Scan bool `json:"scan"`
}
TalkgroupDTO mirrors api.TalkgroupDTO.
type Tone ¶
type Tone struct {
Profile string `json:"profile"`
AlphaTag string `json:"alpha_tag,omitempty"`
System string `json:"system,omitempty"`
GroupID uint32 `json:"group_id,omitempty"`
DeviceSerial string `json:"device_serial"`
MatchedAt time.Time `json:"matched_at"`
FrequenciesHz []float64 `json:"frequencies_hz"`
}
Tone is the payload shape of a `tone.alert` event.