Main Namespace

Aircraft tracking, safety events, alerts, and statistics on the default Socket.IO namespace

Main Namespace

The main namespace (/) is the default connection point for most SkySpy features: aircraft tracking, safety monitoring, custom alerts, ACARS messages, statistics, and airspace data.

Overview

Connect to the main namespace to receive real-time updates. Use topic subscriptions to control which data streams you receive.

📘

Automatic Snapshot

On connection, the server automatically emits aircraft:snapshot with the current state of all aircraft in range. Subscribe to topics to receive ongoing updates.

Topics

Subscribe to one or more topics to receive updates.

TopicDescriptionEventsUse Case
aircraftPosition and state updatesaircraft:*Real-time tracking map
safetySafety events (TCAS, emergencies, conflicts)safety:*Safety monitoring dashboard
statsLive statistics and metricsstats:*Analytics widgets
alertsCustom alert rule triggersalert:*Personalized notifications
acarsACARS datalink messagesacars:*Message decoding
airspaceAirspace advisories and boundariesairspace:*Flight planning
notamsNOTAMs and TFRsnotams:*Airspace restrictions
allAll of the aboveAll eventsComprehensive monitoring

Subscribe Example

// Subscribe to specific topics
socket.emit('subscribe', { 
  topics: ['aircraft', 'safety', 'alerts'] 
});

socket.on('subscribed', (data) => {
  console.log('Subscribed to:', data.topics);
  console.log('Successfully joined:', data.joined);
  if (data.denied) {
    console.warn('Permission denied for:', data.denied);
  }
});
# Subscribe to specific topics
sio.emit('subscribe', {
    'topics': ['aircraft', 'safety', 'alerts']
})

@sio.event
def subscribed(data):
    print('Subscribed to:', data.get('topics'))
    print('Successfully joined:', data.get('joined'))
    if data.get('denied'):
        print('Permission denied for:', data.get('denied'))

Aircraft Events

Track aircraft positions in real-time with snapshot, update, new, and remove events.

EventTriggerPayloadFrequency
aircraft:snapshotOn connect / request{ aircraft[], count, timestamp }Once on connect
aircraft:updatePeriodic updatesFull or batched aircraft list~10 Hz (rate-limited)
aircraft:newNew detectionSingle aircraft objectAs detected
aircraft:removeTimeout / out of range{ hex, reason? }As removed
aircraft:deltaPosition change (if enabled)Delta object with hex and changed fields~10 Hz (rate-limited)
aircraft:heartbeatKeepalive{ count, timestamp }Every 30-60s

Aircraft Payload Fields

FieldTypeDescriptionExample
hexstringICAO 24-bit address (unique identifier)"A1B2C3"
flightstringCallsign (trimmed)"UAL123"
latnumberLatitude in decimal degrees37.7749
lonnumberLongitude in decimal degrees-122.4194
alt_baronumberBarometric altitude in feet35000
alt_geomnumberGeometric (GNSS) altitude in feet35125
gsnumberGround speed in knots450.5
tracknumberTrack angle in degrees (0-359)270.5
squawkstringMode A squawk code"1200"
categorystringAircraft category (e.g., A3=large)"A3"
distance_nmnumberDistance from receiver in nautical miles12.5
seennumberSeconds since last message2.3
rssinumberReceived signal strength indicator (dBFS)-15.2

Aircraft Events Example

const aircraftMap = new Map();

socket.on('aircraft:snapshot', (data) => {
  console.log(`Initial snapshot: ${data.count} aircraft`);
  data.aircraft.forEach(ac => {
    aircraftMap.set(ac.hex, ac);
  });
  renderMap(aircraftMap);
});

socket.on('aircraft:update', (aircraft) => {
  // Update or add aircraft
  if (Array.isArray(aircraft)) {
    aircraft.forEach(ac => aircraftMap.set(ac.hex, ac));
  } else {
    aircraftMap.set(aircraft.hex, aircraft);
  }
  renderMap(aircraftMap);
});

socket.on('aircraft:new', (aircraft) => {
  console.log(`New aircraft: ${aircraft.flight || aircraft.hex}`);
  aircraftMap.set(aircraft.hex, aircraft);
  renderMap(aircraftMap);
});

socket.on('aircraft:remove', (data) => {
  console.log(`Removed: ${data.hex} (${data.reason || 'timeout'})`);
  aircraftMap.delete(data.hex);
  renderMap(aircraftMap);
});

socket.on('aircraft:delta', (delta) => {
  // Merge delta into existing aircraft
  const existing = aircraftMap.get(delta.hex);
  if (existing) {
    Object.assign(existing, delta);
    renderMap(aircraftMap);
  }
});
aircraft_map = {}

@sio.event
def aircraft_snapshot(data):
    print(f"Initial snapshot: {data.get('count')} aircraft")
    for ac in data.get('aircraft', []):
        aircraft_map[ac['hex']] = ac
    render_map(aircraft_map)

@sio.event
def aircraft_update(aircraft):
    # Update or add aircraft
    if isinstance(aircraft, list):
        for ac in aircraft:
            aircraft_map[ac['hex']] = ac
    else:
        aircraft_map[aircraft['hex']] = aircraft
    render_map(aircraft_map)

@sio.event
def aircraft_new(aircraft):
    print(f"New aircraft: {aircraft.get('flight') or aircraft['hex']}")
    aircraft_map[aircraft['hex']] = aircraft
    render_map(aircraft_map)

@sio.event
def aircraft_remove(data):
    hex_code = data.get('hex')
    reason = data.get('reason', 'timeout')
    print(f"Removed: {hex_code} ({reason})")
    aircraft_map.pop(hex_code, None)
    render_map(aircraft_map)

@sio.event
def aircraft_delta(delta):
    # Merge delta into existing aircraft
    hex_code = delta.get('hex')
    if hex_code in aircraft_map:
        aircraft_map[hex_code].update(delta)
        render_map(aircraft_map)

Safety Events

Monitor safety-critical events like TCAS alerts, emergency squawks, and proximity conflicts.

EventTriggerPayload
safety:snapshotInitial state on subscription{ events[], count, timestamp }
safety:eventNew safety event detectedSingle event object

Safety Event Fields

FieldTypeDescription
idstringUnique event ID
event_typestringEvent type: tcas, emergency, conflict, low_altitude, etc.
severitystringSeverity level: critical, high, medium, low
icao_hexstringAircraft ICAO hex code
callsignstringAircraft callsign
messagestringHuman-readable description
detailsobjectEvent-specific details (e.g., conflicting aircraft, altitude)
timestampstringISO 8601 timestamp

Safety Events Example

socket.on('safety:snapshot', (data) => {
  console.log(`Active safety events: ${data.count}`);
  data.events.forEach(event => {
    displaySafetyAlert(event);
  });
});

socket.on('safety:event', (event) => {
  console.warn(`⚠️  ${event.severity.toUpperCase()}: ${event.message}`);
  
  // Play alert sound for critical events
  if (event.severity === 'critical') {
    playAlertSound();
  }
  
  // Show notification
  showNotification({
    title: `Safety Alert: ${event.event_type}`,
    body: event.message,
    icon: 'warning',
    data: event
  });
  
  // Display in UI
  displaySafetyAlert(event);
});
@sio.event
def safety_snapshot(data):
    print(f"Active safety events: {data.get('count')}")
    for event in data.get('events', []):
        display_safety_alert(event)

@sio.event
def safety_event(event):
    severity = event.get('severity', 'unknown').upper()
    message = event.get('message', '')
    print(f"⚠️  {severity}: {message}")
    
    # Play alert sound for critical events
    if event.get('severity') == 'critical':
        play_alert_sound()
    
    # Send notification
    send_notification(
        title=f"Safety Alert: {event.get('event_type')}",
        body=message,
        data=event
    )
    
    # Display in UI
    display_safety_alert(event)

Custom Alerts

Receive notifications when custom alert rules are triggered (geo-fence, altitude, callsign, etc.).

📘

User-Specific

Alert events are sent to authenticated users only. Each user receives alerts for their own rules.

EventTriggerPayload
alert:triggeredAlert rule condition metAlert object with rule and aircraft data
alert:snapshotInitial state on subscriptionList of active alerts

Alerts Example

socket.on('alert:triggered', (alert) => {
  console.log('Alert triggered:', alert.rule_name);
  
  // Show notification
  showNotification({
    title: alert.rule_name,
    body: `${alert.aircraft.flight || alert.aircraft.hex} - ${alert.message}`,
    icon: 'alert',
    data: alert
  });
  
  // Send push notification if enabled
  if (alert.rule.notification_channels.includes('push')) {
    sendPushNotification(alert);
  }
  
  // Log to alert history
  logAlert(alert);
});
@sio.event
def alert_triggered(alert):
    print(f"Alert triggered: {alert.get('rule_name')}")
    
    aircraft = alert.get('aircraft', {})
    flight = aircraft.get('flight') or aircraft.get('hex')
    message = alert.get('message', '')
    
    # Send notification
    send_notification(
        title=alert.get('rule_name'),
        body=f"{flight} - {message}",
        data=alert
    )
    
    # Send push notification if enabled
    rule = alert.get('rule', {})
    if 'push' in rule.get('notification_channels', []):
        send_push_notification(alert)
    
    # Log to alert history
    log_alert(alert)

Request Types

Make on-demand queries using the request event. All request types from the REST API are supported.

Common Request Types

Request TypeParametersDescription
aircrafticaoSingle aircraft by ICAO hex
aircraft_listmilitary_only, category, min_altitude, max_altitudeFiltered aircraft list
aircraft-infoicaoDetailed aircraft metadata (registration, type, operator)
aircraft-info-bulkicaos[]Bulk aircraft info for multiple hex codes
aircraft-statsLive statistics (total, by type, by altitude, etc.)
aircraft-snapshotCurrent aircraft snapshot (same as aircraft:snapshot event)
photoicao, thumbnailAircraft photo URL from external sources
sightingshours, limit, offset, icao_hex, callsignHistorical sightings with pagination

Advanced Request Types

Request TypeParametersDescription
safety-eventsevent_type, severity, hours, limitHistorical safety events with filtering
safety-event-detailid or event_idDetailed event information
safety-acknowledgeid or event_idAcknowledge safety event
alert-rulesUser's alert rules
acars-statsACARS statistics
metarslat, lon, radius_nm, limitMETARs within radius
taflat, lon, radius_nm, limitTAFs within radius
pirepslat, lon, radius_nm, hours, limitPilot reports within radius and time
airportslat, lon, radius_nm, limitAirports within radius
navaidslat, lon, radius_nm, limitNavaids within radius

Request Example

// Request aircraft info
const requestId = `req_${Date.now()}`;
socket.emit('request', {
  type: 'aircraft-info',
  request_id: requestId,
  params: { icao: 'A1B2C3' }
});

socket.on('response', (data) => {
  if (data.request_id === requestId) {
    console.log('Aircraft info:', data.data);
  }
});

// Request sightings with pagination
const sightingsId = `req_${Date.now()}`;
socket.emit('request', {
  type: 'sightings',
  request_id: sightingsId,
  params: {
    hours: 24,
    limit: 50,
    offset: 0,
    icao_hex: 'A1B2C3'
  }
});
import uuid

# Request aircraft info
request_id = f"req_{uuid.uuid4().hex}"
sio.emit('request', {
    'type': 'aircraft-info',
    'request_id': request_id,
    'params': {'icao': 'A1B2C3'}
})

@sio.event
def response(data):
    if data.get('request_id') == request_id:
        print('Aircraft info:', data.get('data'))

# Request sightings with pagination
sightings_id = f"req_{uuid.uuid4().hex}"
sio.emit('request', {
    'type': 'sightings',
    'request_id': sightings_id,
    'params': {
        'hours': 24,
        'limit': 50,
        'offset': 0,
        'icao_hex': 'A1B2C3'
    }
})

Statistics

Subscribe to the stats topic for live analytics updates.

socket.on('stats:update', (stats) => {
  updateDashboard({
    total: stats.total_aircraft,
    military: stats.military_count,
    commercial: stats.commercial_count,
    avgAltitude: stats.avg_altitude,
    maxDistance: stats.max_distance_nm
  });
});
@sio.event
def stats_update(stats):
    update_dashboard(
        total=stats.get('total_aircraft'),
        military=stats.get('military_count'),
        commercial=stats.get('commercial_count'),
        avg_altitude=stats.get('avg_altitude'),
        max_distance=stats.get('max_distance_nm')
    )

Next Steps

📘

Explore More Features