Skip to content

Connection Lifecycle

Understanding the connection sequence helps with debugging and advanced usage.

Overview

Client                                          Radio
  │                                               │
  │──── Are You There (0x03) ────────────────────>│
  │<─── I Am Here (0x04) ─────────────────────────│
  │──── Are You Ready (0x06) ────────────────────>│
  │<─── I Am Ready (0x06) ────────────────────────│
  │                                               │
  │──── Login (0x80 bytes) ──────────────────────>│
  │<─── Auth Response (0x60 bytes) ───────────────│
  │──── Token Ack (0x40 bytes) ──────────────────>│
  │                                               │
  │<─── Radio Conninfo (0x90 bytes) ──────────────│
  │──── Client Conninfo (0x90 bytes) ────────────>│
  │<─── Status (0x50 bytes, CI-V/audio ports) ────│
  │                                               │
  │════ CI-V Port (50002) ════════════════════════│
  │──── OpenClose (open) ────────────────────────>│
  │                                               │
  │──── CI-V commands... ────────────────────────>│
  │<─── CI-V responses... ────────────────────────│
  │                                               │
  │──── Ping (every 500ms) ──────────────────────>│
  │<─── Pong ─────────────────────────────────────│

Phase 1: Discovery

The client sends "Are You There" (type 0x03) packets until the radio responds with "I Am Here" (type 0x04). This exchange establishes the radio's connection ID.

# Automatic — handled by IcomTransport.connect()
await transport.connect("192.168.1.100", 50001)
  • Up to 10 retries, 1 second timeout each
  • Radio responds with its remote_id (4-byte identifier)
  • Followed by "Are You Ready" / "I Am Ready" exchange

Phase 2: Authentication

After discovery, the client sends a login packet (0x80 bytes) containing:

  • Encoded username (position-dependent substitution cipher)
  • Encoded password (same encoding)
  • Client computer name

The radio responds with an auth response (0x60 bytes) containing:

  • Session token (4 bytes) — used for all subsequent authenticated packets
  • Token request ID — must be echoed back
  • Connection type string (e.g., "FTTH")
  • Error code (0 = success)

The client then sends a token acknowledgement (0x40 bytes) to confirm.

Credential Encoding

Credentials are obfuscated using a substitution table, not encrypted. This is Icom's protocol design. See Security for implications.

Phase 3: Conninfo Exchange

The radio sends its conninfo (0x90 bytes) containing its GUID/MAC. The client must:

  1. Extract the radio's GUID (bytes 0x20–0x2F)
  2. Echo it back in the client's own conninfo packet
  3. Include audio codec preferences and port information

This triggers the radio to send a status packet (0x50 bytes) containing:

  • CI-V port number (usually 50002)
  • Audio port number (usually 50003)

For fast non-audio operations, the library proceeds as soon as CI-V is resolved. Audio port resolution is finalized lazily on first audio use.

GUID is Required

If the client doesn't echo the radio's GUID, the status packet will report CI-V port = 0, and commands won't work.

Phase 4: CI-V Data Stream

The library opens a second UDP connection to the CI-V port (50002) and sends an OpenClose packet to start the CI-V data stream.

Once open, CI-V commands flow through port 50002 (not the control port 50001).

CI-V calls are then serialized through an internal commander queue (wfview-style), with pacing and retry logic to reduce real-hardware flakiness.

Keep-Alive

Both ports maintain keep-alive with ping packets every 500ms. If the radio doesn't receive pings, it drops the connection after a timeout.

The library also handles:

  • Retransmit requests — if the radio detects missing packets, it requests retransmission
  • Sequence tracking — all data packets have sequence numbers for ordering and gap detection

Disconnection

Clean disconnect sends:

  1. OpenClose(close) on the CI-V port
  2. Disconnect control packet on the control port
  3. UDP sockets are closed
# Automatic with context manager
async with IcomRadio(...) as radio:
    ...  # Disconnect happens on exit

# Manual
await radio.disconnect()

Connection States

from icom_lan import ConnectionState

# Available states
ConnectionState.DISCONNECTED  # Not connected
ConnectionState.CONNECTING    # Handshake in progress
ConnectionState.CONNECTED     # Ready for commands

Error Handling

from icom_lan import IcomRadio
from icom_lan.exceptions import (
    ConnectionError,
    AuthenticationError,
    TimeoutError,
)

try:
    async with IcomRadio("192.168.1.100", username="u", password="p") as radio:
        freq = await radio.get_frequency()
except ConnectionError as e:
    print(f"Can't reach radio: {e}")
except AuthenticationError as e:
    print(f"Wrong credentials: {e}")
except TimeoutError as e:
    print(f"Radio not responding: {e}")