UNPKG

homebridge-pioneer-avr-2025

Version:

A Pioneer AVR plugin for homebridge. This plugin is designed for Pioneer models that use Telnet Commands (e.g., VSX-922). It may not be compatible with newer models (e.g., VSX-LX304).

264 lines 12.7 kB
"use strict"; // src/discovery.ts 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; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findDevices = findDevices; const net = __importStar(require("net")); const bonjour_1 = __importDefault(require("bonjour")); // Bonjour for mDNS discovery // Main function to discover devices using mDNS (Bonjour) only function findDevices(targetName_1, telnetPorts_1, log_1) { return __awaiter(this, arguments, void 0, function* (targetName, telnetPorts, log, maxDevices = Infinity) { const devices = []; const bonjourService = (0, bonjour_1.default)(); log.debug('Searching for Pioneer Receivers via Bonjour...'); // Discover Bonjour devices with a dynamic stop condition yield discoverBonjourDevices(targetName, devices, telnetPorts, log, bonjourService, maxDevices); // Return the list of devices after Bonjour search completes return devices; }); } // mDNS (Bonjour) Discovery with port check function discoverBonjourDevices(targetName, devices, telnetPorts, log, bonjourService, maxDevices) { return __awaiter(this, void 0, void 0, function* () { const hostsFound = []; return new Promise((resolve) => { bonjourService.find({ type: 'raop' }, (service) => { // { // addresses: [ '192.168.1.99' ], // name: '746E1B312D68@VSX-923', // fqdn: '746E1B312D68@VSX-923._raop._tcp.local', // host: 'VSX-923.local', // port: 1024, // type: 'raop', // protocol: 'tcp', // subtypes: [], // rawTxt: <Buffer 09 74 78 74 76 65 72 73 3d 31 04 63 68 3d 32 06 63 6e 3d 30 2c 31 07 64 61 3d 74 72 75 65 06 65 74 3d 30 2c 34 0c 66 74 3d 30 78 34 34 46 38 41 30 30 ... 97 more bytes>, // txt: { // txtvers: '1', // ch: '2', // cn: '0,1', // da: 'true', // et: '0,4', // ft: '0x44F8A00', // md: '0,1,2', // pw: 'false', // sv: 'false', // sr: '44100', // ss: '16', // tp: 'UDP', // vn: '65537', // vs: '141.9', // am: 'VSX-923', // fv: 's9294.4003.1106' // } // } var _a; let origName = ''; if (service.name) { origName = String(service.name); } let serviceString = 'oops!'; try { serviceString = JSON.stringify(service, null, 2); } catch (error) { log.debug('Failed to convert object to string:', error); if (service.name) { serviceString = service.name; } } if (serviceString.toLowerCase().includes(targetName.toLowerCase())) { if (devices.length >= maxDevices) { bonjourService.destroy(); resolve(); return; } if (service.name) { service.name = service.name.replace(/\.local$/, ''); service.name = service.name.includes('@') ? service.name.split('@')[1] : service.name; } // Function to find the first available IP address in the service object function findIp(service) { for (const key in service) { if (typeof service[key] === 'string' && service[key].match(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/)) { return service[key]; } if (Array.isArray(service[key])) { const ip = service[key].find((item) => item.match(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/)); if (ip) { return ip; } } } return null; } // Function to find a name field in the service object, extracting the part after '@' if present, or using 'host' as a fallback function findName(service) { for (const key in service) { if (key.toLowerCase() === 'name' && typeof service[key] === 'string') { const name = service[key]; // Check if '@' is in the name, and return the part after it if present return name.includes('@') ? name.split('@')[1] : name; } } // Fallback to 'host' if 'name' is not present or doesn't contain '@' if (service.host && typeof service.host === 'string') { // Remove '.local' from the host name if present return service.host.replace(/\.local$/, ''); } return null; } // Attempt to retrieve IP and name with direct assignment and fallbacks const host = service.host || ((_a = service.referer) === null || _a === void 0 ? void 0 : _a.address) || (Array.isArray(service.addresses) ? service.addresses[0] : findIp(service)); const name = service.name || findName(service) || service.host || service.fqdn || host; // name = name.replace(/[^a-zA-Z0-9 ']/g, ''); // name = name // .replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, '') // .trim(); const fqdn = service.fqdn || host; if (!hostsFound.includes(host)) { hostsFound.push(host); checkPorts(host, name, origName, telnetPorts, 'bonjour', fqdn, log).then((device) => { if (device && !devices.some((d) => d.host.toLowerCase() === device.host.toLowerCase() && d.port === device.port)) { devices.push(device); log.debug(`Device with open port found: ${device.name} at ${device.host}:${device.port}`); } // Stop search if the max number of devices has been found if (devices.length >= maxDevices) { bonjourService.destroy(); resolve(); } }); } } }); // Set a timeout to automatically stop the search if it takes too long setTimeout(() => { if (devices.length >= 1) { bonjourService.destroy(); resolve(); } else { setTimeout(() => { // Stop search if one devices has been found if (devices.length >= 1) { bonjourService.destroy(); resolve(); } else { setTimeout(() => { bonjourService.destroy(); resolve(); }, 29000); } }, 1000); } }, 500); }); }); } // Check specific ports for open connections on each device function checkPorts(host, name, origName, ports, source, fqdn, log) { return __awaiter(this, void 0, void 0, function* () { for (const port of ports) { if (yield isPortOpen(host, port, log)) { return { name, origName, host, port, source, fqdn }; // Return device info if port is open } } return null; }); } // Utility to check if a specific port on a device is open, with a connection attempt function isPortOpen(host, port, log) { return __awaiter(this, void 0, void 0, function* () { log.debug(`Checking if port ${port} on host ${host} is open`); return new Promise((resolve) => { const socket = new net.Socket(); let isConnectionOpen = false; // Set a 10-second timeout for the entire operation socket.setTimeout(10000); // Attempt to connect and check if we can access the port via Telnet socket.connect(port, host, () => { log.debug(`Port ${port} on Host ${host} responded; attempting to close connection.`); socket.end(); // Close the connection isConnectionOpen = true; }); // Event: Successful connection and closure socket.on('close', () => { resolve(isConnectionOpen); }); // Event: Error in connection attempt (port likely closed) socket.on('error', (err) => { log.debug(`Error accessing port ${port} on Host ${host}: ${err.message}`); resolve(false); }); // Event: Connection timed out socket.on('timeout', () => { log.debug(`Timeout reached when checking port ${port} on Host ${host}`); socket.destroy(); resolve(false); }); }); }); } //# sourceMappingURL=discovery.js.map