UNPKG

starlink-node

Version:

Node.js package for communication and control of Starlink terminals locally

260 lines 10.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.StarlinkClient = void 0; const grpc = __importStar(require("@grpc/grpc-js")); const protoLoader = __importStar(require("@grpc/proto-loader")); class StarlinkClient { constructor(host = '192.168.100.1', port = 9200) { this.connected = false; this.host = host; this.port = port; } async connect() { if (this.connected) return; try { // Note: In a real implementation, you would need the actual Starlink protobuf definitions // This is a simplified version for demonstration const protoPath = './proto/starlink.proto'; const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); this.client = new protoDescriptor.SpaceX.API.Device.Device(`${this.host}:${this.port}`, grpc.credentials.createInsecure()); this.connected = true; } catch (error) { throw new Error(`Failed to connect to Starlink terminal at ${this.host}:${this.port}: ${error}`); } } async getStatus() { await this.connect(); return new Promise((resolve, reject) => { const request = { getStatus: {} }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } try { const status = response.dishGetStatus; resolve({ uptime: status.deviceInfo?.uptimeS || 0, hardware: { version: status.deviceInfo?.hardwareVersion || 'unknown', dishId: status.deviceInfo?.id || 'unknown', utcOffsetS: status.deviceInfo?.utcOffsetS || 0, }, state: this.mapState(status.state), alertsEnabled: status.alertsEnabled || false, fractionObstructed: status.obstructionStats?.fractionObstructed || 0, secondsUntilSwUpdate: status.secondsUntilSwUpdate || 0, popPingDropRate: status.popPingDropRate || 0, downlinkThroughputBps: status.downlinkThroughputBps || 0, uplinkThroughputBps: status.uplinkThroughputBps || 0, popPingLatencyMs: status.popPingLatencyMs || 0, }); } catch (parseError) { reject(new Error(`Failed to parse status response: ${parseError}`)); } }); }); } async getStats() { await this.connect(); return new Promise((resolve, reject) => { const request = { dishGetStats: {} }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } try { const stats = response.dishGetStats; resolve({ pingStats: { latencyMs: stats.popPingLatencyMs || 0, dropRate: stats.popPingDropRate || 0, jitterMs: stats.popPingJitterMs || 0, }, throughputStats: { downloadBps: stats.downlinkThroughputBps || 0, uploadBps: stats.uplinkThroughputBps || 0, }, obstructionStats: { fractionObstructed: stats.obstructionStats?.fractionObstructed || 0, validS: stats.obstructionStats?.validS || 0, wedgeFractionObstructed: stats.obstructionStats?.wedgeFractionObstructed || [], wedgeAbsFractionObstructed: stats.obstructionStats?.wedgeAbsFractionObstructed || [], }, }); } catch (parseError) { reject(new Error(`Failed to parse stats response: ${parseError}`)); } }); }); } async getLocation() { await this.connect(); return new Promise((resolve, reject) => { const request = { getLocation: {} }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } try { const location = response.getLocation; resolve({ latitude: location.lla?.lat, longitude: location.lla?.lon, altitude: location.lla?.alt, obstructionData: { fractionObstructed: location.obstructionStats?.fractionObstructed || 0, timeObstructed: location.obstructionStats?.timeObstructed || 0, validS: location.obstructionStats?.validS || 0, }, }); } catch (parseError) { reject(new Error(`Failed to parse location response: ${parseError}`)); } }); }); } async getHistory() { await this.connect(); return new Promise((resolve, reject) => { const request = { dishGetHistory: {} }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } try { const history = response.dishGetHistory; resolve({ currentHourlyStats: { downloadThroughputBps: history.currentHourlyStats?.downloadThroughputBps || [], uploadThroughputBps: history.currentHourlyStats?.uploadThroughputBps || [], pingLatencyMs: history.currentHourlyStats?.pingLatencyMs || [], pingDropRate: history.currentHourlyStats?.pingDropRate || [], obstructedS: history.currentHourlyStats?.obstructedS || [], }, popPingStats: { latencyMs: history.popPingStats?.latencyMs || [], dropRate: history.popPingStats?.dropRate || [], }, }); } catch (parseError) { reject(new Error(`Failed to parse history response: ${parseError}`)); } }); }); } async reboot() { await this.connect(); return new Promise((resolve, reject) => { const request = { reboot: {} }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } resolve(); }); }); } async stow() { await this.connect(); return new Promise((resolve, reject) => { const request = { dishStow: { unstow: false } }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } resolve(); }); }); } async unstow() { await this.connect(); return new Promise((resolve, reject) => { const request = { dishStow: { unstow: true } }; this.client.Handle(request, (error, response) => { if (error) { reject(this.createStarlinkError(error)); return; } resolve(); }); }); } async disconnect() { if (this.client && this.connected) { this.client.close(); this.connected = false; } } mapState(state) { const stateMap = { 'UNKNOWN': 'OFFLINE', 'OFFLINE': 'OFFLINE', 'BOOTING': 'BOOTING', 'SEARCHING': 'SEARCHING', 'CONNECTED': 'CONNECTED', 'STOWED': 'STOWED', 'THERMAL_SHUTDOWN': 'THERMAL_SHUTDOWN', }; return stateMap[state] || 'OFFLINE'; } createStarlinkError(grpcError) { const error = new Error(grpcError.message); error.code = grpcError.code; error.details = grpcError.details; return error; } } exports.StarlinkClient = StarlinkClient; //# sourceMappingURL=client.js.map