proxy backend

This commit is contained in:
Oli Passey
2025-12-21 16:54:13 +00:00
parent 1fb43156e8
commit 77bf4ffd05
30 changed files with 3010 additions and 79 deletions

View File

@@ -0,0 +1,168 @@
import Bonjour from 'bonjour-service';
import { WledClient } from '../wled/client';
export interface DiscoveredDevice {
name: string;
ipAddress: string;
port: number;
host: string;
type: string;
}
export class DiscoveryService {
private bonjour: Bonjour;
constructor() {
this.bonjour = new Bonjour();
}
/**
* Discover WLED devices on the network using mDNS
* @param timeout - How long to search for devices (in milliseconds)
* @returns Array of discovered devices
*/
async discoverDevices(timeout: number = 5000): Promise<DiscoveredDevice[]> {
return new Promise((resolve) => {
const discoveredDevices: DiscoveredDevice[] = [];
const seenAddresses = new Set<string>();
// Search for WLED devices using mDNS service discovery
// WLED devices advertise themselves as _http._tcp
const browser = this.bonjour.find({ type: 'http' });
browser.on('up', (service) => {
// WLED devices typically have "wled" in their name or txt records
const isWled =
service.name?.toLowerCase().includes('wled') ||
service.txt?.ver?.startsWith('0.') || // WLED version format
service.txt?.name !== undefined;
if (isWled && service.addresses && service.addresses.length > 0) {
// Use the first IPv4 address
const ipv4Address = service.addresses.find((addr: string) =>
addr.includes('.') && !addr.startsWith('169.254')
);
if (ipv4Address && !seenAddresses.has(ipv4Address)) {
seenAddresses.add(ipv4Address);
discoveredDevices.push({
name: service.name || service.host || 'Unknown WLED Device',
ipAddress: ipv4Address,
port: service.port || 80,
host: service.host || ipv4Address,
type: service.type || 'http',
});
}
}
});
// Stop searching after timeout
setTimeout(() => {
browser.stop();
resolve(discoveredDevices);
}, timeout);
});
}
/**
* Scan a range of IP addresses for WLED devices
* This is a fallback method when mDNS doesn't work
* @param baseIp - Base IP address (e.g., "192.168.1")
* @param startRange - Start of range (e.g., 1)
* @param endRange - End of range (e.g., 254)
* @returns Array of discovered devices
*/
async scanIpRange(
baseIp: string,
startRange: number = 1,
endRange: number = 254
): Promise<DiscoveredDevice[]> {
const discoveredDevices: DiscoveredDevice[] = [];
const promises: Promise<void>[] = [];
for (let i = startRange; i <= endRange; i++) {
const ipAddress = `${baseIp}.${i}`;
promises.push(
(async () => {
try {
const client = new WledClient(ipAddress, 80);
const info = await Promise.race([
client.getInfo(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 2000)
),
]) as any;
if (info && info.name) {
discoveredDevices.push({
name: info.name,
ipAddress,
port: 80,
host: ipAddress,
type: 'wled',
});
}
} catch (err) {
// Device not found or not responding, ignore
}
})()
);
// Process in batches of 10 to avoid overwhelming the network
if (promises.length >= 10) {
await Promise.all(promises);
promises.length = 0;
}
}
// Wait for remaining promises
if (promises.length > 0) {
await Promise.all(promises);
}
return discoveredDevices;
}
/**
* Verify a device at a specific IP address is a WLED device
* @param ipAddress - IP address to check
* @param port - Port number (default: 80)
* @returns Device info if it's a WLED device, null otherwise
*/
async verifyDevice(
ipAddress: string,
port: number = 80
): Promise<DiscoveredDevice | null> {
try {
const client = new WledClient(ipAddress, port);
const info = await Promise.race([
client.getInfo(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 3000)
),
]) as any;
if (info && info.name) {
return {
name: info.name,
ipAddress,
port,
host: ipAddress,
type: 'wled',
};
}
return null;
} catch (err) {
return null;
}
}
destroy() {
this.bonjour.destroy();
}
}
export const discoveryService = new DiscoveryService();