UNPKG

homebridge-bondbridge

Version:

Plugin to integrate BondBridge units by Bond to Homekit

298 lines (260 loc) 8.89 kB
// This script is to generate a complete configuration file needed for the bondbridge plugin // This script can handle up to 3 independent BondBridge (BB) systems // // This script is invoked from the plugin when homebridge restart // Read CLI args const args = process.argv.slice(2); const BBIP1 = args[0]; const BBtoken1 = args[1]; const CFsetupOption1 = args[2]; const CFtimerSetup1 = args[3]; const BBdebug1 = args[4]; const BBIP2 = args[5]; const BBtoken2 = args[6]; const CFsetupOption2 = args[7]; const CFtimerSetup2 = args[8]; const BBdebug2 = args[9]; const BBIP3 = args[10]; const BBtoken3 = args[11]; const CFsetupOption3 = args[12]; const CFtimerSetup3 = args[13]; const BBdebug3 = args[14]; const BONDBRIDGE_SH_PATH = args[15]; // Globals for config parts let bondbridgeBasicKeys = {}; let bondbridgeModelQueue = {}; let bondbridgeConstants = { constants: [] }; let bondbridgeQueueTypes = { queueTypes: [] }; let bondbridgeAccessories = { accessories: [] }; // Helper: IP validation function isValidIp(ip) { return /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/.test(ip); } if (!isValidIp(BBIP1)) { console.warn(`WARNING: the specified IP address ${BBIP1} is in wrong format`); process.exit(1); } if (BBIP2 && BBIP2 !== "undefined" && !isValidIp(BBIP2)) { console.warn(`WARNING: the specified IP address ${BBIP2} is in wrong format`); process.exit(1); } if (BBIP3 && BBIP3 !== "undefined" && !isValidIp(BBIP3)) { console.warn(`WARNING: the specified IP address ${BBIP3} is in wrong format`); process.exit(1); } const noOfBondBridges = [BBIP1, BBIP2, BBIP3].filter(ip => ip && ip !== "undefined").length; // Functions creating config parts function createBasicKeys(debugFlag) { bondbridgeBasicKeys = { title: "BondBridge config auto-generated by ConfigCreator", debug: debugFlag, outputConstants: false, statusMsg: true, timeout: 60000, stateChangeResponseTime: 0 }; } function createModelQueue(model, bondid, queue) { bondbridgeModelQueue = { manufacturer: "OLIBRA", model, serialNumber: bondid, queue }; } function createConstants(IPA, ip, debug) { const debugA = debug === "true" ? "-debug" : ""; const constant = { key: IPA, value: `${ip}${debugA}` }; bondbridgeConstants.constants.push(constant); } function createQueueTypes(queue) { queueType = { queue: queue, queueType: "WoRm2" }; bondbridgeQueueTypes.queueTypes.push(queueType); } function createFan(name, minStep, modelQueue, bondToken, device, IPA) { const fan = { type: "Fan", displayName: name, on: false, rotationSpeed: 25, name, ...modelQueue, polling: [ { characteristic: "on" }, { characteristic: "rotationSpeed" } ], props: { rotationSpeed: { minStep } }, state_cmd: `'${BONDBRIDGE_SH_PATH}'`, state_cmd_suffix: `fan 'token:${bondToken}' 'device:${device}' ${IPA}` }; bondbridgeAccessories.accessories.push(fan); } function createLightbulbNoDimmer(name, accType, modelQueue, bondToken, device, IPA) { const lightbulb = { type: "Lightbulb", displayName: name, on: false, name, ...modelQueue, polling: [{ characteristic: "on" }], state_cmd: `'${BONDBRIDGE_SH_PATH}'`, state_cmd_suffix: `${accType} 'token:${bondToken}' 'device:${device}' ${IPA}` }; bondbridgeAccessories.accessories.push(lightbulb); } function createLightbulbWithDimmer(name, accType, minStep, modelQueue, bondToken, device, IPA) { const lightbulb = { type: "Lightbulb", displayName: name, on: false, brightness: 80, name, ...modelQueue, polling: [ { characteristic: "on" }, { characteristic: "brightness" } ], props: { brightness: { minStep } }, state_cmd: `'${BONDBRIDGE_SH_PATH}'`, state_cmd_suffix: `${accType} 'token:${bondToken}' 'device:${device}' ${IPA}` }; bondbridgeAccessories.accessories.push(lightbulb); } function createTimerLightbulb(name, accType, deviceType, modelQueue, bondToken, timerDevice, device, IPA) { const timerLightbulb = { type: "Lightbulb", displayName: name, on: false, brightness: 0, name, ...modelQueue, polling: [ { characteristic: "on" }, { characteristic: "brightness" } ], props: { brightness: { minStep: 1 } }, state_cmd: `'${BONDBRIDGE_SH_PATH}'`, state_cmd_suffix: `${accType} 'token:${bondToken}' 'device:${timerDevice}' '${deviceType}:${device}' ${IPA}` }; bondbridgeAccessories.accessories.push(timerLightbulb); } function assembleBondBridgeConfig() { return { name: "BondBridge", ...bondbridgeBasicKeys, ...bondbridgeConstants, ...bondbridgeQueueTypes, ...bondbridgeAccessories, platform: "BondBridge" }; } async function main() { const debugBondBridge = [BBdebug1, BBdebug2, BBdebug3].some(d => d === "true"); for (let n = 1; n <= noOfBondBridges; n++) { const IPA = `\${BBIP${n}}`; const ip = eval(`BBIP${n}`); const bondToken = eval(`BBtoken${n}`); const CFsetupOption = eval(`CFsetupOption${n}`); const CFtimerSetup = eval(`CFtimerSetup${n}`); const debug = eval(`BBdebug${n}`); const queue = ['BBA', 'BBB', 'BBC'][n - 1]; // Fetch version info let version; try { const res = await fetch(`http://${ip}/v2/sys/version`); if (!res.ok) throw new Error(`HTTP error ${res.status}`); version = await res.json(); } catch (err) { console.error(`ERROR: BondBridge device is inaccessible or IP ${ip} is invalid!`); process.exit(1); } const bondid = version.bondid; if (!bondid) { console.error("ERROR: Missing bondid in response!"); process.exit(1); } const model = version.model || ""; // Create config parts createBasicKeys(debugBondBridge); createModelQueue(model, bondid, queue); if (CFsetupOption !== "doNotConfigure") { createConstants(IPA, ip, debug); createQueueTypes(queue); } // Fetch devices let devicesData; try { const devRes = await fetch(`http://${ip}/v2/devices`, { headers: { "BOND-Token": bondToken } }); if (!devRes.ok) throw new Error(`HTTP error ${devRes.status}`); devicesData = await devRes.json(); } catch (err) { console.error(`ERROR: Failed to fetch devices from ${ip}`, err); continue; } const deviceKeys = Object.keys(devicesData); for (const device of deviceKeys) { if (!/^[a-f0-9]+$/.test(device)) continue; const timerDevice = device.split('').reverse().join(''); // Get max_speed let maxSpeed = 0; try { const propRes = await fetch(`http://${ip}/v2/devices/${device}/properties`, { headers: { "BOND-Token": bondToken } }); if (!propRes.ok) throw new Error(`HTTP error ${propRes.status}`); const propJson = await propRes.json(); maxSpeed = propJson.max_speed || 0; } catch {} const speedInterval = maxSpeed ? Math.floor(100 / maxSpeed) : 0; // Get device name let name = ""; try { const nameRes = await fetch(`http://${ip}/v2/devices/${device}`, { headers: { "BOND-Token": bondToken } }); if (!nameRes.ok) throw new Error(`HTTP error ${nameRes.status}`); const nameJson = await nameRes.json(); name = nameJson.name || ""; } catch {} if (CFsetupOption !== "doNotConfigure") { if (/^[a-zA-Z0-9 ]*Fan$/.test(name)) { if (CFsetupOption !== "lightDimmer") { createFan(name, speedInterval, bondbridgeModelQueue, bondToken, device, IPA); } if (CFtimerSetup === "includeTimers") { createTimerLightbulb(`${name} Timer`, "fanTimer", "fanDevice", bondbridgeModelQueue, bondToken, timerDevice, device, IPA); } } if (/^[a-zA-Z0-9 ]*Light$/.test(name)) { if (CFsetupOption === "fanLight") { createLightbulbNoDimmer(name, "light", bondbridgeModelQueue, bondToken, device, IPA); } else if (CFsetupOption === "fanLightDimmer") { createLightbulbWithDimmer(name, "light", speedInterval, bondbridgeModelQueue, bondToken, device, IPA); } else if (CFsetupOption === "lightDimmer") { createLightbulbWithDimmer(`${name} Dimmer`, "dimmer", speedInterval, bondbridgeModelQueue, bondToken, device, IPA); } if (CFtimerSetup === "includeTimers" && CFsetupOption !== "fan") { createTimerLightbulb(`${name} Timer`, "lightTimer", "lightDevice", bondbridgeModelQueue, bondToken, timerDevice, device, IPA); } } } } } const bondbridgeConfig = assembleBondBridgeConfig(); console.log("DONE! createBondBridgeConfig completed successfully!"); console.log(JSON.stringify(bondbridgeConfig)); process.exit(0); } main();