/** * PhyHub Direct Connection Service * * Allows direct connection to PhyHub for testing purposes, * bypassing the phyos layer that normally runs on devices. * * Usage: * - Set PHYHUB_DIRECT=true to enable direct connection * - Provide DEVICE_ID and ACCESS_KEY environment variables * - Optionally set PHYHUB_URL (defaults to EU region) */ import { Socket } from 'socket.io-client'; import { getSocketIOWithProxy } from '@phystack/socket.io-proxy'; export interface DirectConnectionConfig { deviceId: string; accessKey: string; phyhubUrl?: string; } const DEFAULT_PHYHUB_URLS: Record = { eu: 'https://phyhub.eu.omborigrid.net', us: 'https://phyhub.us.omborigrid.net', uae: 'https://phyhub.uae.omborigrid.net', au: 'https://phyhub.au.omborigrid.net', in: 'https://phyhub.in.omborigrid.net', qa: 'https://phyhub.qa.omborigrid.net', dev: 'https://phyhub.dev.omborigrid.net', local: 'http://localhost:3000', }; export class PhyHubDirectConnection { private socket: Socket | null = null; private config: DirectConnectionConfig; constructor(config: DirectConnectionConfig) { this.config = { ...config, phyhubUrl: config.phyhubUrl || DEFAULT_PHYHUB_URLS.eu, }; } /** * Create a direct connection from environment variables * @throws Error if required environment variables are missing */ static fromEnv(): PhyHubDirectConnection { const deviceId = process.env.DEVICE_ID || process.env.PHYGRID_DEVICE_ID; const accessKey = process.env.ACCESS_KEY || process.env.PHYGRID_DEVICE_KEY; if (!deviceId) { throw new Error('[PhyHubDirectConnection] DEVICE_ID or PHYGRID_DEVICE_ID env var required'); } if (!accessKey) { throw new Error('[PhyHubDirectConnection] ACCESS_KEY or PHYGRID_DEVICE_KEY env var required'); } const region = process.env.PHYHUB_REGION?.toLowerCase(); const phyhubUrl = process.env.PHYHUB_URL || (region ? DEFAULT_PHYHUB_URLS[region] : undefined); if (!phyhubUrl) { throw new Error(`[PhyHubDirectConnection] PHYHUB_URL or valid PHYHUB_REGION required. Valid regions: ${Object.keys(DEFAULT_PHYHUB_URLS).join(', ')}`); } return new PhyHubDirectConnection({ deviceId, accessKey, phyhubUrl, }); } /** * Check if direct connection mode is enabled */ static isEnabled(): boolean { return process.env.PHYHUB_DIRECT === 'true' || process.env.PHYHUB_DIRECT === '1'; } /** * Connect directly to PhyHub */ async connect(): Promise { if (this.socket?.connected) { return this.socket; } const { deviceId, accessKey, phyhubUrl } = this.config; console.log(`[PhyHubDirectConnection] Connecting to ${phyhubUrl}`); console.log(`[PhyHubDirectConnection] Device ID: ${deviceId}`); this.socket = await getSocketIOWithProxy(phyhubUrl!, { auth: { deviceId, accessKey, }, reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 1000, timeout: 10000, }); await new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Connection timeout')); }, 15000); this.socket!.on('connect', () => { clearTimeout(timeout); console.log(`[PhyHubDirectConnection] Connected successfully`); resolve(); }); this.socket!.on('connect_error', (error) => { clearTimeout(timeout); console.error(`[PhyHubDirectConnection] Connection error:`, error.message); reject(error); }); }); return this.socket; } } export default PhyHubDirectConnection;