diff --git a/lib/KNXInterface.js b/lib/KNXInterface.js index be8930c..82f6db3 100644 --- a/lib/KNXInterface.js +++ b/lib/KNXInterface.js @@ -41,7 +41,7 @@ class KNXInterface extends EventEmitter { // Function to create the KNX connection createConnection() { // The KNX tunnel connection itself through the knx library - if (this.isConnected === true) return; // Skip re-initializing when already connected. + if (this.isConnected === true && !this.isTimedOut) return; // Skip re-initializing when already connected and healthy. try { this.knxConnection = new knx.Connection({ ipAddr: this.ipAddress, // IP address obtained through interfacemanager @@ -71,6 +71,7 @@ class KNXInterface extends EventEmitter { disconnected: () => { this.log('KNX Disconnected'); this.isConnected = false; + this.isTimedOut = false; this.onKNXConnectionCallbacks.forEach((callback) => { try { @@ -125,6 +126,7 @@ class KNXInterface extends EventEmitter { // Safely disconnect and reconnect the KNX connection _reconnect() { + this.isTimedOut = false; if (!this.knxConnection) { this.isConnected = false; this.createConnection(); @@ -235,9 +237,11 @@ class KNXInterface extends EventEmitter { return this.knxCommunicationQueue.add(async () => { // Only add the request in the queue when the connection is openend. return new Promise((resolve, reject) => { - this.knxConnection.read(groupaddress, (src, data) => resolve(data)); - // Trigger a timeout, inherited from the library timeout (3s) - setTimeout(() => reject(new Error(`knx_read_timeout: ${groupaddress}`)), 3000); + const timeout = setTimeout(() => reject(new Error(`knx_read_timeout: ${groupaddress}`)), 3000); + this.knxConnection.read(groupaddress, (src, data) => { + clearTimeout(timeout); + resolve(data); + }); }); }); } diff --git a/lib/KNXInterfaceManager.js b/lib/KNXInterfaceManager.js index 63c0ff2..1380b67 100644 --- a/lib/KNXInterfaceManager.js +++ b/lib/KNXInterfaceManager.js @@ -6,7 +6,7 @@ const ip = require('ip'); const KNXInterface = require('./KNXInterface'); // 3 minute search loop -// const FIND_DEVICES_INTERVAL = 3 * (60 * 1000); +const FIND_DEVICES_INTERVAL = 3 * (60 * 1000); class KNXInterfaceManager extends EventEmitter { @@ -25,7 +25,7 @@ class KNXInterfaceManager extends EventEmitter { this.KNXInterfaces = {}; if (ip.isV4Format(localIP)) { - this.localIPBuffer = ip.toBuffer(ip.address()); + this.localIPBuffer = ip.toBuffer(localIP); const interfaces = this.homey.settings.get('interfaces') || []; this.findKNXInterfaces() @@ -48,12 +48,12 @@ class KNXInterfaceManager extends EventEmitter { return; } - // this.findDevicesInterval = setInterval(() => { - // this.findKNXInterfaces() - // .catch(error => { - // this.log('findKNXinterfaces error', error); - // }); - // }, FIND_DEVICES_INTERVAL); + this.findDevicesInterval = setInterval(() => { + this.findKNXInterfaces() + .catch((error) => { + this.log('findKNXinterfaces error', error); + }); + }, FIND_DEVICES_INTERVAL); // Respond on an emit. Add the found interface to the list of interfaces. this.on('interface_found', (knxInterface) => { @@ -220,10 +220,9 @@ class KNXInterfaceManager extends EventEmitter { // When an message is received, parse it for KNXIP data const knxInterface = this.parseKNXResponse(msg); if (!knxInterface) { - return reject(new Error('No valid KNXnet response')); + return null; // Skip non-KNX messages, keep listening for valid responses } // If the data was valid, emit the found interface - // here should be an extra catch to handle other not search/check related KNX messages try { if (this.KNXInterfaces[knxInterface.interfaceMac]) { this.log('Updating', knxInterface.interfaceName); @@ -238,10 +237,8 @@ class KNXInterfaceManager extends EventEmitter { this.homey.settings.set('interfaces', interfaces); this.emit('interface_found', this.KNXInterfaces[knxInterface.interfaceMac]); - return resolve(this.KNXInterfaces[knxInterface.interfaceMac]); } catch (error) { this.log('Creating IP interface instance failed: ', error); - reject(error); } return null; }) @@ -288,7 +285,6 @@ class KNXInterfaceManager extends EventEmitter { - Send the description request to obtain the device information These actions mimics the traffic that ETS uses to check a IP interface */ - this.interfaceFound = false; if (this.searchRunning === false) { this.log('Checking if', ipAddress, 'is a KNX IP interface'); this.searchRunning = true; @@ -319,6 +315,8 @@ class KNXInterfaceManager extends EventEmitter { const udpSocket = dgram.createSocket('udp4'); // Create the socket connections return new Promise((resolve, reject) => { + let interfaceFound = false; + udpSocket .on('error', (err) => { // If the udp server errors, close the connection @@ -361,12 +359,16 @@ class KNXInterfaceManager extends EventEmitter { } catch (e) { // socket may already be closed } - this.interfaceFound = true; + interfaceFound = true; this.searchRunning = false; try { - // Use the Interface MAC as the key value - this.KNXInterfaces[knxInterface.interfaceMac] = new KNXInterface(knxInterface); + // Update existing interface or create a new one + if (this.KNXInterfaces[knxInterface.interfaceMac]) { + this.KNXInterfaces[knxInterface.interfaceMac].updateIP(knxInterface.interfaceIp); + } else { + this.KNXInterfaces[knxInterface.interfaceMac] = new KNXInterface(knxInterface); + } const interfaces = this.getSimpleInterfaceList(); this.homey.settings.set('interfaces', interfaces); this.emit('interface_found', this.KNXInterfaces[knxInterface.interfaceMac]); @@ -389,7 +391,7 @@ class KNXInterfaceManager extends EventEmitter { .bind(36711); setTimeout(() => { - if (!this.interfaceFound) { + if (!interfaceFound) { try { udpSocket.close(); } catch (error) {