UNPKG

signalk-mcp-server

Version:
526 lines 21 kB
import { EventEmitter } from 'events'; import type { SignalKClientOptions, SignalKValue, SignalKDelta, AISTarget, ActiveAlarm, VesselState, AISTargetsResponse, ActiveAlarmsResponse, ConnectionStatus, AvailablePathsResponse, PathValueResponse } from './types/index.js'; export declare class SignalKClient extends EventEmitter { hostname: string; port: number; useTLS: boolean; originalUrl: string; context: string; connected: boolean; latestValues: Map<string, SignalKValue>; availablePaths: Set<string>; aisTargets: Map<string, AISTarget>; activeAlarms: Map<string, ActiveAlarm>; private client; private token?; constructor(options?: SignalKClientOptions); /** * Set SignalK connection configuration from environment variables with sensible defaults * * Environment Variables: * - SIGNALK_HOST: Hostname/IP (default: 'localhost') * - SIGNALK_PORT: Port number (default: 3000) * - SIGNALK_TLS: Use secure connections - true/false (default: false) * * Sets instance properties: * - hostname: The server hostname/IP (e.g., 'localhost', '192.168.1.100') * - port: The server port (e.g., 3000, 443, 80) * - useTLS: Whether to use secure connections (WSS/HTTPS vs WS/HTTP) * * @param options - Override options */ setSignalKConfig(options?: SignalKClientOptions): void; /** * Build WebSocket URL for streaming connections * @returns WebSocket URL (ws:// or wss://) */ buildWebSocketUrl(): string; /** * Build HTTP URL for REST API calls * @returns HTTP URL (http:// or https://) */ buildHttpUrl(): string; /** * Build SignalK REST API URL for a specific vessel and path * @param vesselContext - Vessel context (e.g., 'self', 'urn:mrn:imo:mmsi:123456789') * @param path - SignalK path in dot notation (e.g., 'navigation.position') * @returns Complete REST API URL */ buildRestApiUrl(vesselContext?: string, path?: string): string; /** * Build fetch options with authentication headers if token is configured * @returns RequestInit object with authorization header if token exists */ private buildFetchOptions; /** * Sets up WebSocket event handlers for connection, disconnection, errors, and delta messages * * Event handlers: * - 'connect': Sets connected flag and emits 'connected' event * - 'disconnect': Clears connected flag and emits 'disconnected' event * - 'error': Logs errors and emits 'error' event * - 'delta': Processes incoming SignalK delta messages with vessel data updates * * @example * const client = new SignalKClient(); * client.on('connected', () => console.log('Connected to SignalK')); * client.on('delta', (delta) => console.log('Received data:', delta)); */ setupEventHandlers(): void; /** * Establishes connection to SignalK server (HTTP-only mode) * * This method now operates in HTTP-only mode for maximum data freshness. * WebSocket functionality is preserved but disabled for future streaming capabilities * when MCP servers support real-time data streams. * * Features: * - Tests HTTP connectivity to SignalK server * - Sets connected status based on HTTP availability * - WebSocket code preserved for future streaming implementation * * @returns Promise that resolves when HTTP connection is verified * * @example * const client = new SignalKClient({ hostname: 'localhost', port: 3000 }); * try { * await client.connect(); * console.log('Connected successfully'); * } catch (error) { * console.error('Connection failed:', error); * } */ connect(): Promise<void>; /** * Fetches initial complete vessel state via HTTP API to populate cache immediately * * This method is called after WebSocket connection to ensure getVesselState() * has immediate access to complete vessel data instead of waiting for deltas. * * Features: * - HTTP GET to /signalk/v1/api/vessels/self for complete state * - Populates latestValues Map with all available paths * - Preserves current timestamp for each value * - Updates availablePaths Set automatically * - Graceful error handling - logs errors but doesn't throw * * @returns Promise that resolves when initial state is fetched and cached * * @private */ private fetchInitialVesselState; /** * Recursively populates latestValues Map from SignalK API response data * * This helper method traverses the nested SignalK data structure and * extracts all value objects, storing them in the latestValues Map * with proper full path keys (context.path). * * @param obj - The SignalK data object to traverse * @param context - The vessel context (e.g., 'vessels.self') * @param pathPrefix - Current path prefix being built * * @private */ private populateLatestValuesFromData; /** * Processes incoming SignalK delta messages and updates internal data stores * * Delta message processing: * - Updates latest values cache with timestamps * - Tracks AIS targets from other vessels * - Monitors system notifications and alarms * - Discovers available data paths automatically * - Emits 'delta' event for external listeners * * @param delta - SignalK delta message with vessel updates * * @example * // Delta messages are received automatically via WebSocket * client.on('delta', (delta) => { * console.log('Vessel context:', delta.context); * console.log('Updates:', delta.updates); * }); */ handleDelta(delta: SignalKDelta): void; /** * Processes individual value updates from SignalK delta messages * * Update processing: * - Stores latest values with full path keys (context.path) * - Maintains set of available data paths * - Updates AIS target data for other vessels * - Processes notification/alarm state changes * - Preserves timestamps and source information * * @param message - SignalK delta message containing updates array * * @example * // Updates are processed automatically from delta messages: * // { * // "context": "vessels.self", * // "updates": [{ * // "timestamp": "2023-06-22T10:30:15Z", * // "values": [{ * // "path": "navigation.position", * // "value": {"latitude": 37.8199, "longitude": -122.4783} * // }] * // }] * // } */ processUpdates(message: SignalKDelta): void; /** * Updates AIS target information for other vessels detected in the area * * AIS data tracking: * - Creates new target entries for unknown vessels * - Updates existing targets with latest position/course/speed data * - Maintains MMSI identifier and last update timestamp * - Supports any SignalK path (position, course, speed, name, etc.) * * @param vesselContext - Vessel context (e.g., 'vessels.urn:mrn:imo:mmsi:123456789') * @param path - SignalK data path (e.g., 'navigation.position') * @param value - The data value for this path * @param timestamp - ISO timestamp of the update * * @example * // AIS targets are updated automatically from delta messages: * // Context: "vessels.urn:mrn:imo:mmsi:123456789" * // Path: "navigation.position" * // Value: {"latitude": 37.8200, "longitude": -122.4800} * * const targets = client.getAISTargets(); * console.log('Nearby vessels:', targets.targets.length); */ updateAISTarget(vesselContext: string, path: string, value: any, timestamp: string): void; /** * Updates active alarm and notification states from SignalK notification paths * * Alarm processing: * - Adds alarms when state is not 'normal' (alert, warn, alarm, emergency) * - Removes alarms when state returns to 'normal' or null * - Preserves alarm message and metadata * - Tracks timestamp of alarm state changes * * @param path - Notification path (e.g., 'notifications.engines.temperature') * @param value - Notification object with state and message * @param timestamp - ISO timestamp of the notification * * @example * // Alarms are updated automatically from notification paths: * // Path: "notifications.engines.temperature" * // Value: { * // "state": "alert", * // "message": "Engine temperature high", * // "method": ["visual", "sound"] * // } * * const alarms = client.getActiveAlarms(); * console.log('Active alarms:', alarms.count); */ updateAlarms(path: string, value: any, timestamp: string): void; /** * Returns current vessel state with all available sensor data, navigation information, and vessel identity * * This method fetches fresh data directly from the SignalK HTTP API on each request, * ensuring that stale cached data is never returned. The response includes: * - All SignalK paths for the current vessel context (vessels.self by default) * - Vessel identity information (name, MMSI, call sign) * - Position, heading, speed, wind, engine data, etc. * - Latest values with timestamps and source information * - Connection status and context information * * @returns Promise<VesselState> object with fresh data from SignalK server * * @example * const state = await client.getVesselState(); * console.log('Vessel name:', state.data['name']?.value); * console.log('Position:', state.data['navigation.position']?.value); * console.log('Speed:', state.data['navigation.speedOverGround']?.value); * console.log('Wind:', state.data['environment.wind']?.value); * * // Example response: * // { * // "connected": true, * // "context": "vessels.self", * // "timestamp": "2023-06-22T10:30:15.123Z", * // "data": { * // "name": { * // "value": "My Vessel", * // "timestamp": "2023-06-22T10:30:15.000Z", * // "source": "vessel-identity" * // }, * // "navigation.position": { * // "value": {"latitude": 37.8199, "longitude": -122.4783}, * // "timestamp": "2023-06-22T10:30:15.000Z", * // "source": {"label": "GPS1", "type": "NMEA0183"} * // }, * // "navigation.speedOverGround": { * // "value": 5.2, * // "timestamp": "2023-06-22T10:30:15.000Z" * // } * // } * // } */ getVesselState(): Promise<VesselState>; /** * Calculates the distance between two geographic coordinates using the Haversine formula * * @param lat1 - Latitude of first point * @param lon1 - Longitude of first point * @param lat2 - Latitude of second point * @param lon2 - Longitude of second point * @returns Distance in meters */ private calculateDistance; /** * Pattern-based filtering for AIS data fields * Determines if a SignalK path should be included in AIS target data * * @param path - SignalK data path to check * @returns true if the path should be included, false otherwise */ private shouldIncludeAISPath; /** * Returns nearby AIS targets (other vessels) with their position and navigation data * * This method fetches fresh AIS data directly from the SignalK HTTP API on each request, * ensuring that stale cached data is never returned. The response includes: * - Only vessels with proper MMSI identifiers (true AIS targets) * - Position, course, speed, and vessel identification * - Distance in meters from self vessel (when positions available) * - Sorted by proximity (closest vessels first) * - Supports pagination with configurable page size * - Only includes targets updated within last 5 minutes * * @param page - Page number (1-based, default: 1) * @param pageSize - Number of targets per page (default: 10, max: 50) * @returns Promise<AISTargetsResponse> with array of nearby vessels * * @example * const targets = await client.getAISTargets(1, 10); * console.log(`Found ${targets.count} nearby vessels`); * console.log(`Page ${targets.pagination.page} of ${targets.pagination.totalPages}`); * * targets.targets.forEach(target => { * console.log(`MMSI: ${target.mmsi}`); * if (target.distanceMeters) { * console.log(`Distance: ${target.distanceMeters}m`); * } * if (target['navigation.position']) { * console.log(`Position: ${target['navigation.position'].value.latitude}, ${target['navigation.position'].value.longitude}`); * } * }); * * // Example response: * // { * // "connected": true, * // "count": 2, * // "timestamp": "2023-06-22T10:30:15.123Z", * // "targets": [ * // { * // "mmsi": "123456789", * // "distanceMeters": 1852.5, * // "navigation.position": { * // "value": {"latitude": 37.8200, "longitude": -122.4800}, * // "timestamp": "2023-06-22T10:29:45.000Z" * // }, * // "lastUpdate": "2023-06-22T10:29:45.000Z" * // } * // ], * // "pagination": { * // "page": 1, * // "pageSize": 10, * // "totalCount": 15, * // "totalPages": 2, * // "hasNextPage": true, * // "hasPreviousPage": false * // } * // } */ getAISTargets(page?: number, pageSize?: number): Promise<AISTargetsResponse>; /** * Returns all alarms and system notifications including resolved (normal state) alarms * * This method fetches fresh alarm data directly from the SignalK HTTP API on each request, * ensuring that stale cached data is never returned. The response includes: * - All notification paths from the current vessel * - Alarm states: alert, warn, alarm, emergency, and normal (resolved) * - Notification messages and metadata * - Fresh timestamps for each notification * * @returns Promise<ActiveAlarmsResponse> with array of all notifications * * @example * const alarms = await client.getActiveAlarms(); * console.log(`${alarms.count} total alarms (including resolved)`); * * // Filter for only critical alarms * const criticalAlarms = alarms.alarms.filter(alarm => * alarm.state !== 'normal' * ); * console.log(`${criticalAlarms.length} critical alarms`); * * alarms.alarms.forEach(alarm => { * console.log(`${alarm.state}: ${alarm.message || 'No message'}`); * console.log(`Path: ${alarm.path}`); * console.log(`Time: ${alarm.timestamp}`); * }); * * // Example response: * // { * // "connected": true, * // "count": 2, * // "timestamp": "2023-06-22T10:30:15.123Z", * // "alarms": [ * // { * // "path": "notifications.engines.temperature", * // "state": "normal", * // "message": "Engine temperature normal", * // "timestamp": "2023-06-22T10:25:30.000Z" * // }, * // { * // "path": "notifications.battery.voltage", * // "state": "alert", * // "message": "Battery voltage low", * // "timestamp": "2023-06-22T10:28:45.000Z" * // } * // ] * // } */ getActiveAlarms(): Promise<ActiveAlarmsResponse>; /** * Discovers and returns all available SignalK data paths on the server * * Path discovery: * - Primary: Uses HTTP REST API to get complete path list from server * - Fallback: Uses WebSocket-discovered paths if HTTP fails * - Filters out metadata fields ($schema, meta, timestamp) * - Returns sorted alphabetical list of available data paths * * @returns Promise<AvailablePathsResponse> with array of available paths * * @example * const pathsResponse = await client.listAvailablePaths(); * console.log(`${pathsResponse.count} paths available`); * * pathsResponse.paths.forEach(path => { * console.log(`Available: ${path}`); * }); * * // Example response: * // { * // "connected": true, * // "count": 25, * // "timestamp": "2023-06-22T10:30:15.123Z", * // "paths": [ * // "electrical.batteries.house.voltage", * // "environment.wind.speedApparent", * // "navigation.courseOverGround", * // "navigation.position", * // "navigation.speedOverGround", * // "propulsion.main.temperature" * // ] * // } */ listAvailablePaths(): Promise<AvailablePathsResponse>; /** * Gets the latest value for a specific SignalK data path * * Value retrieval: * - Primary: Uses HTTP REST API for real-time data from server * - Fallback: Uses WebSocket-cached value if HTTP fails * - Returns complete value object with metadata * - Supports any valid SignalK path * * @param path - SignalK data path in dot notation (e.g., 'navigation.position') * @returns Promise<PathValueResponse> with latest value and metadata * * @example * // Get current position * const position = await client.getPathValue('navigation.position'); * console.log('Latitude:', position.data.value.latitude); * console.log('Longitude:', position.data.value.longitude); * * // Get wind speed * const windSpeed = await client.getPathValue('environment.wind.speedApparent'); * console.log('Wind speed:', windSpeed.data.value, 'm/s'); * * // Get engine temperature * const engineTemp = await client.getPathValue('propulsion.main.temperature'); * console.log('Engine temp:', engineTemp.data.value, 'K'); * * // Example response: * // { * // "connected": true, * // "path": "navigation.position", * // "timestamp": "2023-06-22T10:30:15.123Z", * // "data": { * // "value": { * // "latitude": 37.8199, * // "longitude": -122.4783 * // }, * // "timestamp": "2023-06-22T10:30:15.000Z", * // "source": { * // "label": "GPS1", * // "type": "NMEA0183" * // } * // } * // } */ getPathValue(path: string): Promise<PathValueResponse>; /** * Returns comprehensive connection status and client configuration information * * This method now reflects HTTP-only mode status. WebSocket information is * preserved for future streaming support but not actively used. * * Status information: * - HTTP connection state (verified during connect()) * - Server URLs (both WebSocket and HTTP for reference) * - Configuration details (hostname, port, TLS) * - Vessel context being monitored * * Note: Cache statistics (pathCount, aisTargetCount, activeAlarmCount) will * always be 0 in HTTP-only mode as data is fetched fresh on each request. * * @returns ConnectionStatus object with detailed connection information * * @example * const status = client.getConnectionStatus(); * console.log('Connected:', status.connected); * console.log('Server:', status.hostname + ':' + status.port); * console.log('TLS:', status.useTLS); * console.log('HTTP URL:', status.httpUrl); * * // Example response: * // { * // "connected": true, * // "url": "http://localhost:3000", * // "wsUrl": "ws://localhost:3000", // Preserved for future use * // "httpUrl": "http://localhost:3000", * // "hostname": "localhost", * // "port": 3000, * // "useTLS": false, * // "context": "vessels.self", * // "pathCount": 0, // Always 0 in HTTP-only mode * // "aisTargetCount": 0, // Always 0 in HTTP-only mode * // "activeAlarmCount": 0, // Always 0 in HTTP-only mode * // "timestamp": "2023-06-22T10:30:15.123Z" * // } */ getConnectionStatus(): ConnectionStatus; /** * Cleanly disconnects from the SignalK server * * In HTTP-only mode, this simply sets the connected flag to false. * The WebSocket disconnect is preserved for future streaming support. * * @example * // Disconnect when done * client.disconnect(); * console.log('Disconnected from SignalK server'); */ disconnect(): void; } //# sourceMappingURL=signalk-client.d.ts.map