UNPKG

homebridge-myplace

Version:

Exec Plugin bringing Advanatge Air MyPlace system to Homekit

154 lines (135 loc) 6.37 kB
// Update myplaceConfig const { isIpAccessible } = require("./isIpAccessible"); const { devicesAutoDiscovery } = require("./devicesAutoDiscovery"); const { createMyPlaceConfig } = require("./createMyPlaceConfig"); const { readConfig } = require("./readConfig"); const chalk = require("chalk"); // Helper function to delay execution function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } const isValidIp = (value) => /^(?:\d{1,3}\.){3}\d{1,3}$/.test(value); async function updateConfig(config, log, storagePath, pluginPath) { // Enforce maxAccessories: default 149 (Homebridge hard limit) const maxAccessories = (typeof config.maxAccessories === "number" && config.maxAccessories > 0 && config.maxAccessories < 150) ? config.maxAccessories : 149; // Check if devices are missing IPs const devicesMissingIPs = Array.isArray(config.devices) && config.devices.some(d => !d.ipAddress); // Default ports to scan let portsToTry = [2025, 10211]; let doDevicesAutoDiscovery = false; let devicesAutoDiscoveryCounter = 0; // Insert custom device ports (user-configured) to the front if (Array.isArray(config.devices)) { config.devices.slice().reverse().forEach(device => { if (device.port) { portsToTry = portsToTry.filter(p => p !== device.port); // remove duplicates portsToTry.unshift(device.port); } }); } let IPs = []; if (!Array.isArray(config.devices) || devicesMissingIPs) { log.warn(`⚠️ No devices found in the original config!`); log.warn(`🔍 *** Triggering device auto-discovery...`); doDevicesAutoDiscovery = true; } else { // check if the device(s) in config is accessbile or not with retry up to 5 times, if not, set it to "undefined". log.warn(`🕵️ *** Validating device IP address(es)...`); const noOfDevices = config.devices.length; for ( let i = 0; i < noOfDevices; i++ ) { const ip = config.devices[i].ipAddress; const port = config.devices[i].port || 2025; if (ip) { IPs.push(`${ip}:${port}`); if ( !isValidIp(ip) ) { log.warn(`⚠️ Device ${i + 1}/${noOfDevices} with IP ${ip}:${port} has its IP in wrong format!`); log.warn(`⚠️ Device ${i + 1}/${noOfDevices} will NOT be processed!`); IPs[i] = "undefined"; } else { const isIpAccessibleTest = await isIpAccessible( `${ip}:${port}`, i, noOfDevices, log ); if ( !isIpAccessibleTest ) { log.warn(`⚠️ Device ${i + 1}/${noOfDevices} with IP ${ip}:${port} is inaccessible! May be power OFF, wrong IP or wrong port.`); log.warn(`⚠️ Device ${i + 1}/${noOfDevices} will NOT be processed!`); IPs[i] = "undefined"; } else { log.info(`✅ Device ${i + 1}/${noOfDevices} is reachable!`); } } } else { IPs.push("undefined"); } } // check that not all IPs are "undefined", if so, do devucesAutoDiscovery... if (IPs.every((el) => el === "undefined")) { // final attempt to auto discover a Bond device log.warn(`⚠️ No specified device is accessible on the LAN network!`); log.warn(`🔍 *** Triggering device auto-discovery...`); doDevicesAutoDiscovery = true; } } // Auto-discovery stage if ( doDevicesAutoDiscovery ) { log.info("🔍 Ports to scan for devices:", portsToTry); const foundDevices = await devicesAutoDiscovery(config, log, portsToTry); const noOfDevices = foundDevices.length; if (noOfDevices > 0) { config.devices = foundDevices; log.info("Devices config:\n" + JSON.stringify(config.devices, null, 2)); // Store found devices IPs for ( let i = 0; i < noOfDevices; i++ ) { IPs[i] = foundDevices[i].ipAddress + ':' + foundDevices[i].port; } } else { log.warn("⚠️ No devices found on any ports."); // check if an existing config.json is present in this.storagePath/.myplace, if so use it existingConfig = readConfig( storagePath, log ); if (existingConfig) { log.warn("⚠️ Proceeding with existing config — all cached accessories will be restored."); return existingConfig; } else { log.warn("⚠️ Proceeding with original config — no accessories will be created and cached accessories will be removed!"); return config; } } } // Run CreateMyPlaceConfig log.warn(`*** Running createMyPlaceConfig...`); try { const noOfDevices = IPs.length; const noOfDevicesProcessed = IPs.filter(ip => ip !== "undefined").length; const myplaceConfig = await createMyPlaceConfig(config, IPs, pluginPath, log); if (doDevicesAutoDiscovery) { log.info(`✅ DONE! createMyPlaceConfig completed successfully for ${noOfDevicesProcessed}/${noOfDevices} "auto-discovered" device(s)!`); } else { log.info(`✅ DONE! createMyPlaceConfig completed successfully for ${noOfDevicesProcessed}/${noOfDevices} device(s)!`); } log.debug("Updated MyPlace config:\n" + JSON.stringify(myplaceConfig)); // Enforce accessories limit if (Array.isArray(myplaceConfig.accessories) && myplaceConfig.accessories.length > maxAccessories) { log.warn(`⚠️ Configured accessories exceed limit of ${maxAccessories}. ` + `Only the first ${maxAccessories} will be bridged, ` + `${myplaceConfig.accessories.length - maxAccessories} ignored.`); log.info("Note: Homebridge only allows a maximum of 149 bridged accessories per bridge."); myplaceConfig.accessories = myplaceConfig.accessories.slice(0, maxAccessories); } return myplaceConfig; } catch (err) { log.warn(`⚠️ ${err.message}`); log.warn(`⚠️ Config not updated!`); // check if an existing config.json is present in this.storagePath/.myplace, if so use it existingConfig = readConfig( storagePath, log ); if (existingConfig) { log.warn(`⚠️ Proceeding with existing config — all cached accessories will be restored.`); return existingConfig; } log.warn(`⚠️ Proceeding with original config — no accessories will be created and cached accessories will be removed!`); return config; } return config; // fallback } module.exports = { updateConfig };